diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000000..29a1f38c2d3 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,38 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.4.0 + hooks: + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace + - repo: https://github.com/psf/black + rev: 21.4b2 + hooks: + - id: black + - repo: https://github.com/pycqa/flake8 + rev: 3.9.1 + hooks: + - id: flake8 + entry: flake8 --max-line-length=122 --ignore=E203,W503 + - repo: https://github.com/codespell-project/codespell + rev: v2.0.0 + hooks: + - id: codespell + entry: codespell + args: [ '--ignore-words-list="mape","ba","hist"', '--quiet-level=2', '--skip=".git,*.yaml,*.json,./tests"' ] + - repo: https://github.com/pre-commit/mirrors-mypy + rev: 'v0.812' + hooks: + - id: mypy + args: [ --ignore-missing-imports ] + - repo: https://github.com/pre-commit/mirrors-pylint + rev: 'v3.0.0a3' + hooks: + - id: pylint + entry: pylint terminal.py gamestonk_terminal tests + - repo: https://github.com/Yelp/detect-secrets + rev: v1.1.0 + hooks: + - id: detect-secrets + args: [ '--baseline', '.secrets.baseline' ] + exclude: package.lock.json diff --git a/.secrets.baseline b/.secrets.baseline new file mode 100644 index 00000000000..10a1fe2c470 --- /dev/null +++ b/.secrets.baseline @@ -0,0 +1,103 @@ +{ + "version": "1.1.0", + "plugins_used": [ + { + "name": "ArtifactoryDetector" + }, + { + "name": "AWSKeyDetector" + }, + { + "name": "AzureStorageKeyDetector" + }, + { + "name": "Base64HighEntropyString", + "limit": 4.5 + }, + { + "name": "BasicAuthDetector" + }, + { + "name": "CloudantDetector" + }, + { + "name": "HexHighEntropyString", + "limit": 3.0 + }, + { + "name": "IbmCloudIamDetector" + }, + { + "name": "IbmCosHmacDetector" + }, + { + "name": "JwtTokenDetector" + }, + { + "name": "KeywordDetector", + "keyword_exclude": "" + }, + { + "name": "MailchimpDetector" + }, + { + "name": "NpmDetector" + }, + { + "name": "PrivateKeyDetector" + }, + { + "name": "SlackDetector" + }, + { + "name": "SoftlayerDetector" + }, + { + "name": "SquareOAuthDetector" + }, + { + "name": "StripeDetector" + }, + { + "name": "TwilioKeyDetector" + } + ], + "filters_used": [ + { + "path": "detect_secrets.filters.allowlist.is_line_allowlisted" + }, + { + "path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies", + "min_level": 2 + }, + { + "path": "detect_secrets.filters.heuristic.is_indirect_reference" + }, + { + "path": "detect_secrets.filters.heuristic.is_likely_id_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_lock_file" + }, + { + "path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_potential_uuid" + }, + { + "path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign" + }, + { + "path": "detect_secrets.filters.heuristic.is_sequential_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_swagger_file" + }, + { + "path": "detect_secrets.filters.heuristic.is_templated_secret" + } + ], + "results": {}, + "generated_at": "2021-05-03T16:53:30Z" +} diff --git a/README.md b/README.md index 4e558e73cf5..5d6ddd3a4e6 100644 --- a/README.md +++ b/README.md @@ -20,13 +20,13 @@ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) ![Discord Shield](https://discordapp.com/api/guilds/831165782750789672/widget.png?style=shield) - +[![Twitter](https://img.shields.io/twitter/url/https/twitter.com/gamestonkt.svg?style=social&label=Follow%20%40gamestonkt)](https://twitter.com/gamestonkt)

- Logo + Logo

Gamestonk Terminal 🚀

@@ -88,6 +88,8 @@ As a modern Python-based environment, GamestonkTerminal opens access to numerous ## Getting Started ### Install +If you'd like to see a video recording of the installation process, @JohnnyDankseed has made one available [here](https://www.twitch.tv/videos/1008414642). + This project was originally written and tested with Python 3.6.8. It should now support Python 3.6, 3.7, and 3.8. Our current recommendation is to use this project with Anaconda's Python distribution - either full [__Anaconda3 Latest__](https://repo.anaconda.com/archive/) or [__Miniconda3 Latest__](https://repo.anaconda.com/archive/). Several features in this project utilize Machine Learning. Machine Learning Python dependencies are optional. If you decided to add Machine Learning features at a later point, you will likely have better user experience with Anaconda's Python distribution. @@ -158,7 +160,7 @@ python terminal.py ``` 10. (Windows - Optional) Speeding up opening process in the future -After you've installed Gamestonk Terminal, you'll find a file named "Gamestonk Terminal.bat". You can use this file to open Gamestonk Terminal quicker. This file can be moved to your desktop if you'd like. If you run into issues while trying to run the batch file. If you run into issues with the batch files, edit the file and check to see if the directories match up. This file assumes you used the default directories when installing. +After you've installed Gamestonk Terminal, you'll find a file named "Gamestonk Terminal.bat". You can use this file to open Gamestonk Terminal quicker. This file can be moved to your desktop if you'd like. If you run into issues while trying to run the batch file. If you run into issues with the batch files, edit the file and check to see if the directories match up. This file assumes you used the default directories when installing. **NOTE:** When you close the terminal and re-open it, the only command you need to re-call is `conda activate gst` before you call `python terminal.py` again. @@ -227,10 +229,12 @@ These are the ones where a key is necessary: * Tradier: https://developer.tradier.com/getting_started * Oanda API: https://developer.oanda.com * CoinMarketCap API: https://coinmarketcap.com/api/ + * Finhub API: https://finnhub.io + * Binance: https://binance.us (US) / https://binance.com (Outside US) When these are obtained, don't forget to update [config_terminal.py](/gamestonk_terminal/config_terminal.py). -Alternatively, you can also set them to the following environment variables: GT_API_KEY_ALPHAVANTAGE, GT_API_KEY_FINANCIALMODELINGPREP, GT_API_KEY_QUANDL, GT_API_REDDIT_CLIENT_ID, GT_API_REDDIT_CLIENT_SECRET, GT_API_REDDIT_USERNAME, GT_API_REDDIT_USER_AGENT, GT_API_REDDIT_PASSWORD, GT_API_TWITTER_KEY, GT_API_TWITTER_SECRET_KEY, GT_API_TWITTER_BEARER_TOKEN, GT_API_POLYGON_KEY, GT_FRED_API_KEY, GT_API_NEWS_TOKEN, GT_OANDA_TOKEN, GT_OANDA_ACCOUNT, GT_TRADIER_TOKEN, GT_CMC_API_KEY. +Alternatively, you can also set them to the following environment variables: GT_API_KEY_ALPHAVANTAGE, GT_API_KEY_FINANCIALMODELINGPREP, GT_API_KEY_QUANDL, GT_API_REDDIT_CLIENT_ID, GT_API_REDDIT_CLIENT_SECRET, GT_API_REDDIT_USERNAME, GT_API_REDDIT_USER_AGENT, GT_API_REDDIT_PASSWORD, GT_API_TWITTER_KEY, GT_API_TWITTER_SECRET_KEY, GT_API_TWITTER_BEARER_TOKEN, GT_API_POLYGON_KEY, GT_FRED_API_KEY, GT_API_NEWS_TOKEN, GT_OANDA_TOKEN, GT_OANDA_ACCOUNT, GT_TRADIER_TOKEN, GT_CMC_API_KEY, GT_CMC_API_KEY, API_FINNHUB_KEY, GT_API_BINANCE_KEY, GT_API_BINANCE_SECRET. Example: ``` @@ -295,9 +299,9 @@ Recommended if you bought the dip, and the share price keeps dipping. You may as 1. Fork the Project 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) -4. Appease the linters and commit again if needed - 1. Install and run `black` for every change you've made - 2. Install and run `flake8` for every change you've made. `flake8 . --count --ignore=E203,W503 --max-line-length=122 --show-source --statistics` +4. Install the pre-commit hooks by running: + ```pre-commit install```. + Any time you commit a change, linters will be run automatically. On changes, you will have to re-commit. 5. Push to your Branch (`git push origin feature/AmazingFeature`) 6. Open a Pull Request @@ -339,15 +343,18 @@ Trading in financial instruments involves high risks including the risk of losin [James Maslek](https://www.linkedin.com/in/james-maslek-b6810186/) - jmaslek11@gmail.com -Feel free to share loss porn, memes or any questions: https://discord.gg/Up2QGbMKHY +Feel free to share loss porn, memes or any questions at: +* Discord: https://discord.gg/Up2QGbMKHY +* Twitter: [@gamestonkt](https://twitter.com/gamestonkt) ## Acknowledgments -* [VICE article](https://www.vice.com/en/article/qjp9vp/gamestonk-terminal-is-a-diy-meme-stock-version-of-bloomberg-terminal) -* [Daily Fintech article](https://dailyfintech.com/2021/02/25/never-underestimate-bloomberg-but-here-are-5-reasons-why-the-gamestonk-terminal-is-a-contender/) -* [HackerNews](https://news.ycombinator.com/item?id=26258773) -* [Reddit r/algotrading](https://www.reddit.com/r/algotrading/comments/m4uvza/gamestonk_terminal_the_next_best_thing_after/) -* [Reddit r/Python](https://www.reddit.com/r/Python/comments/m515yk/gamestonk_terminal_the_equivalent_to_an/) +* [VICE article - Gamestonk Terminal Is a DIY, Meme Stock Version of Bloomberg Terminal](https://www.vice.com/en/article/qjp9vp/gamestonk-terminal-is-a-diy-meme-stock-version-of-bloomberg-terminal) +* [Daily Fintech article - Never underestimate Bloomberg, but here are 5 reasons why the Gamestonk Terminal is a contender](https://dailyfintech.com/2021/02/25/never-underestimate-bloomberg-but-here-are-5-reasons-why-the-gamestonk-terminal-is-a-contender/) +* [HackerNews - Show HN: Can’t afford Bloomberg Terminal? No prob, I built the next best thing ](https://news.ycombinator.com/item?id=26258773) +* [Reddit r/algotrading - Gamestonk Terminal: The next best thing after Bloomberg Terminal.](https://www.reddit.com/r/algotrading/comments/m4uvza/gamestonk_terminal_the_next_best_thing_after/) +* [Reddit r/Python - Gamestonk Terminal: The equivalent to an open-source python Bloomberg Terminal.](https://www.reddit.com/r/Python/comments/m515yk/gamestonk_terminal_the_equivalent_to_an/) +* [Reddit r/Superstonk - Move over Bloomberg Terminal, here comes Gamestonk Terminal](https://www.reddit.com/r/Superstonk/comments/mx2cjh/move_over_bloomberg_terminal_here_comes_gamestonk/) diff --git a/ROADMAP.md b/ROADMAP.md index f872efde669..f9fa2be7b1e 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -40,6 +40,7 @@ * [x] Add latest and trending news commands (@hinxx) - [PR #347](https://github.com/DidierRLopes/GamestonkTerminal/pull/347) * [x] Add top ratings updates (@hinxx) - [PR #357](https://github.com/DidierRLopes/GamestonkTerminal/pull/357) * [x] Add dark pool (ATS) data of tickers with growing trades activity (@didier) - [PR #372](https://github.com/DidierRLopes/GamestonkTerminal/pull/372) +* [x] Past and future IPOs calendar (@didier) - [PR #432](https://github.com/DidierRLopes/GamestonkTerminal/pull/432) **NEXT** @@ -49,6 +50,7 @@ ## Behavioral Analysis * [x] Add FinBrain sentiment from news (@didier) - [PR #223](https://github.com/DidierRLopes/GamestonkTerminal/pull/223) +* [x] Sentiment stats from Finnhub (@didier) - [PR #433](https://github.com/DidierRLopes/GamestonkTerminal/pull/433) **NEXT** * [ ] Implement a better Twitter client (@aia) @@ -78,15 +80,17 @@ * [x] Add technical summary report provided by FinBrain (@didier) - [PR #294](https://github.com/DidierRLopes/GamestonkTerminal/pull/294) * [x] Add recommendation based on technical indicators from Tradingview (@didier) - [PR #301](https://github.com/DidierRLopes/GamestonkTerminal/pull/301) * [x] Add view of stock historical price with trendlines (support, resistance) by Finviz (@didier) - [PR #317](https://github.com/DidierRLopes/GamestonkTerminal/pull/317) +* [x] Add auto-recognition of major TA patterns (@didier) - [PR #429](https://github.com/DidierRLopes/GamestonkTerminal/pull/429) **NEXT** -* [ ] Add auto-recognition of major TA patterns (@didier) + --- ## Due Diligence * [x] Add dark pools (ATS) vs Non-ATS data over time (@didier) - [PR #363](https://github.com/DidierRLopes/GamestonkTerminal/pull/363) -* [x] Add failure to deliver command (@hinxx) - [PR #366](#https://github.com/DidierRLopes/GamestonkTerminal/pull/366) +* [x] Add failure to deliver command (@hinxx) - [PR #366](https://github.com/DidierRLopes/GamestonkTerminal/pull/366) +* [x] Ratings over time (@didier) - [PR #431](https://github.com/DidierRLopes/GamestonkTerminal/pull/431) **NEXT** @@ -171,6 +175,7 @@ ___ ## Economy * [x] Implement Economic data (gpd, unemployment rate, ...) (@jmaslek) - [PR #167](https://github.com/DidierRLopes/GamestonkTerminal/pull/167) * [x] Refactor FRED to ECON menu and add VIX view (@jmaslek) - [PR #405](https://github.com/DidierRLopes/GamestonkTerminal/pull/405) +* [x] Output economic impact events calendar (@didier) - [PR #430](https://github.com/DidierRLopes/GamestonkTerminal/pull/430) **NEXT** @@ -195,13 +200,13 @@ ___ --- ## Cryptocurrencies -* [x] Add Coingecko (@jmaslek) - [PR #283](#https://github.com/DidierRLopes/GamestonkTerminal/pull/283) +* [x] Add Coingecko (@jmaslek) - [PR #283](https://github.com/DidierRLopes/GamestonkTerminal/pull/283) * [x] view top coins from coinmarketcap (@jmaslek) - [PR #378](https://github.com/DidierRLopes/GamestonkTerminal/pull/378) +* [x] Add binance (@jmaslek) [PR #428](#https://github.com/DidierRLopes/GamestonkTerminal/pull/428) **NEXT** * [ ] Add [Coinpaprika](https://coinpaprika.com/api/) * [ ] Add crypto charts -* [ ] Add binance * [ ] Add top altcoin lists --- diff --git a/gamestonk_terminal/README.md b/gamestonk_terminal/README.md index 5034728b90c..8a5f6c790ae 100644 --- a/gamestonk_terminal/README.md +++ b/gamestonk_terminal/README.md @@ -68,6 +68,7 @@ export -f GNUS_data -F csv ## Discover Stocks [»](discovery/README.md) Command|Description|Source ---|---|--- +`ipo` |past and future IPOs |[Finnhub](https://finnhub.io) `map` |S&P500 index stocks map |[Finviz](https://finviz.com) `rtp_sectors` |real-time performance sectors |[Alpha Vantage](www.alphavantage.co) `gainers` |show latest top gainers |[Yahoo Finance](https://finance.yahoo.com/) @@ -95,6 +96,7 @@ Command|Description ----|---- [FinBrain](https://finbrain.tech)| `finbrain` |sentiment from 15+ major news headlines +`stats` |sentiment stats including comparison with sector [Reddit](https://reddit.com)| `wsb` |show what WSB gang is up to in subreddit wallstreetbets `watchlist` |show other users watchlist @@ -183,6 +185,7 @@ Command | Description | Sources `view` | view historical data and trendlines| [Finviz](https://finviz.com/quote.ashx?t=tsla) `summary` | technical summary report| [FinBrain](https://finbrain.tech) `recom` | recommendation based on Technical Indicators| [Tradingview](https://uk.tradingview.com/widget/technical-analysis/) +`pr` | pattern recognition| [Finnhub](https://finnhub.io) [overlap](https://github.com/twopirllc/pandas-ta/tree/master/pandas_ta/overlap) | `ema` | exponential moving average | [Wikipedia](https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average), [Investopedia](https://www.investopedia.com/terms/e/ema.asp) `sma` |simple moving average | [Wikipedia](https://en.wikipedia.org/wiki/Moving_average#Simple_moving_average_(boxcar_filter)), [Investopedia](https://www.investopedia.com/terms/s/sma.asp) @@ -211,6 +214,7 @@ Command|Description|Source `analyst` |analyst prices and ratings of the company |[Finviz](https://finviz.com/) `rating` |rating of the company from strong sell to strong buy | [FMP](https://financialmodelingprep.com/) `pt` |price targets over time |[Business Insider](https://www.businessinsider.com/) +`rot` |ratings over time |[Finnhub](https://finnhub.io) `est` |quarter and year analysts earnings estimates |[Business Insider](https://www.businessinsider.com/) `ins` |insider activity over time |[Business Insider](https://www.businessinsider.com/) `insider` |insider trading of the company |[Finviz](https://finviz.com/) @@ -274,9 +278,17 @@ Command|Description Command|Description ------ | ------------ -`load`| load cryptocurrency data -`view`| load and view cryptocurrency data +[coingecko](#https://www.coingecko.com/en)| +`load`| load cryptocurrency data +`view`| view loaded cryptocurrency data +`trend`| view top 7 coins +[coinmarketcap](#http://coinmarketcap.com)| `top` | view top coins from coinmarketcap +[binance](#http://binance.us)| +`select` | Select a coin/currency +`book`| show order book +`candle`| get klines/candles and plot +`balance`| show coin balance   @@ -328,19 +340,21 @@ Command|Description|Source   ## Economy [»](econ/README.md) -Command|Description ------- | -------- -`vixcls` | Volatility Index -`gdp` | Gross Domestic Product -`unrate` | Unemployment Rate -`dgs1` | 1-Year Treasury Constant Maturity Rate -`dgs5` | 5-Year Treasury Constant Maturity Rate -`dgs10` | 10-Year Treasury Constant Maturity Rate -`dgs30` | 30-Year Treasury Constant Maturity Rate -`mortgage30us` | 30-Year Fixed Rate Mortgage Average -`fedfunds` | Effective Federal Funds Rate -`aaa` | Moody's Seasoned AAA Corporate Bond Yield -`dexcaus` | Canada / U.S. Foreign Exchange Rate (CAD per 1 USD) +Command|Description|Source +------ | -------- | -------- +`events` | economic impact events | https://finnhub.io +`fred` | display customized FRED data | https://fred.stlouisfed.org +`vixcls` | Volatility Index | https://fred.stlouisfed.org +`gdp` | Gross Domestic Product | https://fred.stlouisfed.org +`unrate` | Unemployment Rate | https://fred.stlouisfed.org +`dgs1` | 1-Year Treasury Constant Maturity Rate | https://fred.stlouisfed.org +`dgs5` | 5-Year Treasury Constant Maturity Rate | https://fred.stlouisfed.org +`dgs10` | 10-Year Treasury Constant Maturity Rate | https://fred.stlouisfed.org +`dgs30` | 30-Year Treasury Constant Maturity Rate | https://fred.stlouisfed.org +`mortgage30us` | 30-Year Fixed Rate Mortgage Average | https://fred.stlouisfed.org +`fedfunds` | Effective Federal Funds Rate | https://fred.stlouisfed.org +`aaa` | Moody's Seasoned AAA Corporate Bond Yield | https://fred.stlouisfed.org +`dexcaus` | Canada / U.S. Foreign Exchange Rate (CAD per 1 USD) | https://fred.stlouisfed.org   @@ -397,7 +411,7 @@ positionbook |display the positionbook if Oanda provides one for the forex instr Command|Description ------ | -------- `ema` | buy when price exceeds EMA(l) -`ema_cross` | buy when EMA(short) > EMA(long) +`ema_cross` | buy when EMA(short) > EMA(long) `rsi` | buy when RSI < low and sell when RSI > high diff --git a/gamestonk_terminal/behavioural_analysis/README.md b/gamestonk_terminal/behavioural_analysis/README.md index 18a7cda7b10..e8e14c1f25e 100644 --- a/gamestonk_terminal/behavioural_analysis/README.md +++ b/gamestonk_terminal/behavioural_analysis/README.md @@ -3,6 +3,9 @@ This menu aims to extrapolate behavioural analysis regarding a pre-loaded ticker, and the usage of the following commands along with an example will be exploited below. * [finbrain](#finbrain) + - sentiment from 15+ major news headlines + * [stats](#stats) + - sentiment stats including comparison with sector [REDDIT](#REDDIT) * [wsb](#wsb) @@ -54,6 +57,15 @@ FinBrain collects the news headlines from 15+ major financial news sources on a Captura de ecrã 2021-03-18, às 12 53 31 +## stats +``` +usage: stats +``` +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. [Source: https://finnhub.io] + +Captura de ecrã 2021-05-03, às 15 26 09 + + # REDDIT ## wsb diff --git a/gamestonk_terminal/behavioural_analysis/ba_controller.py b/gamestonk_terminal/behavioural_analysis/ba_controller.py index e8ae8f60aee..725bbdbfa55 100644 --- a/gamestonk_terminal/behavioural_analysis/ba_controller.py +++ b/gamestonk_terminal/behavioural_analysis/ba_controller.py @@ -14,6 +14,7 @@ from gamestonk_terminal.behavioural_analysis import ( reddit_view, stocktwits_view, finbrain_view, + finnhub_view, ) @@ -41,6 +42,7 @@ class BehaviouralAnalysisController: "queries", "rise", "finbrain", + "stats", ] def __init__(self, ticker: str, start: datetime): @@ -63,6 +65,7 @@ class BehaviouralAnalysisController: print(" quit quit to abandon program") print("") print(" finbrain sentiment from 15+ major news headlines") + print(" stats sentiment stats including comparison with sector") print("") print("Reddit:") print( @@ -218,6 +221,10 @@ class BehaviouralAnalysisController: """Process finbrain command""" finbrain_view.sentiment_analysis(other_args, self.ticker) + def call_stats(self, other_args: List[str]): + """Process stats command""" + finnhub_view.sentiment_stats(other_args, self.ticker) + def menu(ticker: str, start: datetime): """Behavioural Analysis Menu""" diff --git a/gamestonk_terminal/behavioural_analysis/finnhub_view.py b/gamestonk_terminal/behavioural_analysis/finnhub_view.py new file mode 100644 index 00000000000..f719184d096 --- /dev/null +++ b/gamestonk_terminal/behavioural_analysis/finnhub_view.py @@ -0,0 +1,82 @@ +import argparse +from typing import List, Dict +import requests +from gamestonk_terminal import config_terminal as cfg +from gamestonk_terminal.helper_funcs import ( + parse_known_args_and_warn, +) + + +def get_sentiment_stats(ticker: str) -> Dict: + """Get sentiment stats + + Parameters + ---------- + ticker : str + Ticker to get sentiment stats + + Returns + ------- + Dict + Get sentiment stats + """ + response = requests.get( + f"https://finnhub.io/api/v1/news-sentiment?symbol={ticker}&token={cfg.API_FINNHUB_KEY}" + ) + if response.status_code == 200: + return response.json() + + return {} + + +def sentiment_stats(other_args: List[str], ticker: str): + """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 + ---------- + other_args : List[str] + Command line arguments to be processed with argparse + ticker : str + Ticker to get sentiment stats + """ + parser = argparse.ArgumentParser( + add_help=False, + prog="stats", + description=""" + 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. + [Source: https://finnhub.io] + """, + ) + + try: + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + + d_stats = get_sentiment_stats(ticker) + + if d_stats: + print(f"Buzz: {round(100*d_stats['buzz']['buzz'],2)} %") + print(f"News Score: {round(100*d_stats['companyNewsScore'],2)} %") + print("") + print(f"Articles Last Week: {d_stats['buzz']['articlesInLastWeek']}") + print(f"Articles Weekly Average: {d_stats['buzz']['weeklyAverage']}") + print("") + print(f"Bullish: {round(100*d_stats['sentiment']['bullishPercent'],2)} %") + print(f"Bearish: {round(100*d_stats['sentiment']['bearishPercent'],2)} %") + print("") + print( + f"Sector Average Bullish: {round(100*d_stats['sectorAverageBullishPercent'],2)} %" + ) + print( + f"Sector Average News Score: {round(100*d_stats['sectorAverageNewsScore'],2)} %" + ) + else: + print("No sentiment stats found.") + + print("") + + except Exception as e: + print(e, "\n") diff --git a/gamestonk_terminal/config_terminal.py b/gamestonk_terminal/config_terminal.py index dd4092eb45d..3685f88e7dd 100644 --- a/gamestonk_terminal/config_terminal.py +++ b/gamestonk_terminal/config_terminal.py @@ -33,7 +33,7 @@ API_TWITTER_SECRET_KEY = os.getenv("GT_API_TWITTER_SECRET_KEY") or "REPLACE_ME" API_TWITTER_BEARER_TOKEN = os.getenv("GT_API_TWITTER_BEARER_TOKEN") or "REPLACE_ME" # https://fred.stlouisfed.org/docs/api/api_key.html -API_FRED_KEY = os.getenv("GT_FRED_API_KEY") or "REPLACE_ME" +API_FRED_KEY = os.getenv("GT_API_FRED_KEY") or "REPLACE_ME" # https://newsapi.org API_NEWS_TOKEN = os.getenv("GT_API_NEWS_TOKEN") or "REPLACE_ME" @@ -51,11 +51,18 @@ OANDA_ACCOUNT = os.getenv("GT_OANDA_ACCOUNT") or "REPLACE ME" OANDA_TOKEN = os.getenv("GT_OANDA_TOKEN") or "REPLACE ME" # https://tradier.com/products/market-data-api -TRADIER_TOKEN = os.getenv("GT_TRADIER_TOKEN") or "REPLACE_ME" +TRADIER_TOKEN = os.getenv("GT_API_TRADIER_TOKEN") or "REPLACE_ME" # Selenium Webbrowser drivers can be found at https://selenium-python.readthedocs.io/installation.html WEBDRIVER_TO_USE = "chrome" PATH_TO_SELENIUM_DRIVER = None # Replace with "PATH" # https://coinmarketcap.com/api/ -COINMARKETCAP_KEY = os.getenv("GT_CMC_API_KEY") or "REPLACE_ME" +API_CMC_KEY = os.getenv("GT_API_CMC_KEY") or "REPLACE_ME" + +# https://www.binance.com/en/ +API_BINANCE_KEY = os.getenv("GT_API_BINANCE_KEY") or "REPLACE_ME" +API_BINANCE_SECRET = os.getenv("GT_API_BINANCE_SECRET") or "REPLACE_ME" + +# https://finnhub.io +API_FINNHUB_KEY = os.getenv("GT_API_FINNHUB_KEY") or "REPLACE_ME" diff --git a/gamestonk_terminal/cryptocurrency/README.md b/gamestonk_terminal/cryptocurrency/README.md index 4a1b7b48fea..da8832da854 100644 --- a/gamestonk_terminal/cryptocurrency/README.md +++ b/gamestonk_terminal/cryptocurrency/README.md @@ -2,14 +2,29 @@ This menu aims to explore crypto world, and the usage of the following commands along with an example will be exploited below. +[COINGECKO](#COINGECKO) * [load](#load) * load a given coin vs a given currency [CoinGecko] * [view](#view) * plot the loaded crypto data +* [trend](#trend) + * show top 7 trending coins + +[COINMARKETCAP](#COINMARKETCAP) * [top](#top) * view top coins from coinmarketcap [coinmarketcap.com] +[BINANCE](#BINANCE) +* [select](#select) + * select coin/currency to use +* [book](#book) + * show order book +* [candle](#candle) + * get klines/candles and plot +* [balance](#balance) + * show coin balance +# COINGECKO ## load ```` @@ -36,6 +51,17 @@ Plot the loaded crypto data. ![crypto_view](https://user-images.githubusercontent.com/25267873/115787452-20889a80-a3ba-11eb-9216-f7fd1ffc98cf.png) +## trend +```` +usage: trend +```` +Print the top 7 trending coins from coingecko. + +Captura de ecrã 2021-05-03, às 15 07 15 + + +# COINMARKETCAP + ## top ```` @@ -50,3 +76,48 @@ This command displays the top n cryptocurrencies from coinmarketcap.com. crypto +# BINANCE + +## select +```` +usage: select [-c --coin] [-q --quote] +```` + +Select a coin/currency to the current object. Note that in binance, the exchange "ticker" is usually COINCURR, such as BTCEUR for BTC to EURO. There is no USD, but it uses a coin tethered to the USD (USDT), which is the default. Some symbols are a combination of coins, as `ETHBTC` is a valid symbol +* -c/--coin Coin to load. If not specified, BTC will be loaded to prevent errors later. +* -q/--quote Quote currency. Defaults to `USDT` (which is 1-to-1 with USD) + + +## book +```` +usage: book -l/--limit +```` + +Gets and shows the order book for the given coin/currency. Shows the cumulative amount of orders. + +* -l/--limit Number of orders to get on each side. One of [5,10,20,50,100,500,1000,5000]. Defaults to 100. + +![orderbook](https://user-images.githubusercontent.com/25267873/116886857-84fcf280-ac21-11eb-9803-5baa8bceca05.png) + + +## candle +```` +usage: candle [-i --interval] [-l --limit] +```` + +Show candle chart for loaded coin/currency. + +* -i/--interval. Interval for candles. One of [1day,3day,1hour,2hour,4hour,6hour,8hour,12hour,1week,1min,3min,5min,15min,30min,1month]. Defaults to 1day. +* -l/--limit. Number of candles to get. Defaults to 100. + +![candle](https://user-images.githubusercontent.com/25267873/116886993-abbb2900-ac21-11eb-9ff8-b6a8131fdac5.png) + + +## balance +```` +usage: balance +```` + +Shows the current holding balance in your account. + + diff --git a/gamestonk_terminal/cryptocurrency/binance_model.py b/gamestonk_terminal/cryptocurrency/binance_model.py new file mode 100644 index 00000000000..7bfad6acd6f --- /dev/null +++ b/gamestonk_terminal/cryptocurrency/binance_model.py @@ -0,0 +1,259 @@ +"""Binance model""" +__docformat__ = "numpy" + +import argparse +from typing import List +import pandas as pd +import numpy as np +from binance.client import Client +from binance.exceptions import BinanceAPIException +from tabulate import tabulate +from gamestonk_terminal.main_helper import parse_known_args_and_warn +import gamestonk_terminal.config_terminal as cfg +from gamestonk_terminal.cryptocurrency.binance_view import plot_order_book, plot_candles + + +def check_valid_binance_str(symbol: str) -> str: + """Check if symbol is in defined binance""" + client = Client(cfg.API_BINANCE_KEY, cfg.API_BINANCE_SECRET) + try: + client.get_avg_price(symbol=symbol.upper()) + return symbol.upper() + except BinanceAPIException as e: + raise argparse.ArgumentTypeError( + f"{symbol} is not a valid binance symbol" + ) from e + + +# pylint: disable=inconsistent-return-statements +def select_binance_coin(other_args: List[str]): + """Define current_coin from binance + + Parameters + ---------- + other_args: List[str] + Argparse arguments + + Returns + ------- + coin: str + Coin that is defined on binance + """ + parser = argparse.ArgumentParser( + prog="select", + add_help=False, + description="Define the coin to be used from binance", + ) + parser.add_argument( + "-c", "--coin", help="Coin to get", dest="coin", type=str, default="BTC" + ) + parser.add_argument( + "-q", + "--quote", + help="Quote currency (what to view coin vs)", + dest="quote", + type=str, + default="USDT", + ) + try: + if other_args: + if "-" not in other_args[0]: + other_args.insert(0, "-c") + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return None, None + + coin = ns_parser.coin + ns_parser.quote + if check_valid_binance_str(coin): + print(f"{ns_parser.coin.upper()} loaded vs {ns_parser.quote.upper()}") + return ns_parser.coin.upper(), ns_parser.quote.upper() + + except Exception as e: + print(e, "\n") + return None, None + + +def order_book(other_args: List[str], coin: str, currency: str): + """Get order book for currency + + Parameters + ---------- + other_args: List[str] + Argparse arguments + coin: str + Coin to get symbol of + currency : str + Currency against which to check symbol + """ + limit_list = [5, 10, 20, 50, 100, 500, 1000, 5000] + parser = argparse.ArgumentParser( + prog="book", add_help=False, description="Get the order book for selected coin" + ) + parser.add_argument( + "-l", + "--limit", + dest="limit", + help="Limit parameter. Adjusts the weight", + default=100, + type=int, + choices=limit_list, + ) + + try: + if not coin or not currency: + print("Coin needs to be selected prior to this command\n") + return + + coin += currency + + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + client = Client(cfg.API_BINANCE_KEY, cfg.API_BINANCE_SECRET) + market_book = client.get_order_book(symbol=coin, limit=ns_parser.limit) + bids = np.asarray(market_book["bids"], dtype=float) + asks = np.asarray(market_book["asks"], dtype=float) + bids = np.insert(bids, 2, bids[:, 1].cumsum(), axis=1) + asks = np.insert(asks, 2, np.flipud(asks[:, 1]).cumsum(), axis=1) + plot_order_book(bids, asks, coin) + + except Exception as e: + print(e, "\n") + + +def show_candles(other_args: List[str], coin: str, currency: str): + """Get klines/candles for coin + + Parameters + ---------- + other_args: List[str] + Argparse arguments + coin: str + Coin to get symbol of + currency : str + Currency against which to check symbol + """ + client = Client(cfg.API_BINANCE_KEY, cfg.API_BINANCE_SECRET) + + interval_map = { + "1day": client.KLINE_INTERVAL_1DAY, + "3day": client.KLINE_INTERVAL_3DAY, + "1hour": client.KLINE_INTERVAL_1HOUR, + "2hour": client.KLINE_INTERVAL_2HOUR, + "4hour": client.KLINE_INTERVAL_4HOUR, + "6hour": client.KLINE_INTERVAL_6HOUR, + "8hour": client.KLINE_INTERVAL_8HOUR, + "12hour": client.KLINE_INTERVAL_12HOUR, + "1week": client.KLINE_INTERVAL_1WEEK, + "1min": client.KLINE_INTERVAL_1MINUTE, + "3min": client.KLINE_INTERVAL_3MINUTE, + "5min": client.KLINE_INTERVAL_5MINUTE, + "15min": client.KLINE_INTERVAL_15MINUTE, + "30min": client.KLINE_INTERVAL_30MINUTE, + "1month": client.KLINE_INTERVAL_1MONTH, + } + interval_choices = list(interval_map.keys()) + + parser = argparse.ArgumentParser( + prog="candle", + add_help=False, + description="Program to plot candles for binance data", + ) + parser.add_argument( + "-i", + "--interval", + help="Interval to get data", + choices=interval_choices, + dest="interval", + default="1day", + type=str, + ) + + parser.add_argument( + "-l", "--limit", dest="limit", default=100, help="Number to get", type=int + ) + + try: + if not coin or not currency: + print("Coin needs to be selected prior to this command\n") + return + + coin += currency + + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + + candles = client.get_klines( + symbol=coin, + interval=interval_map[ns_parser.interval], + limit=ns_parser.limit, + ) + + # Response + # 1499040000000, // Open time + # "0.01634790", // Open + # "0.80000000", // High + # "0.01575800", // Low + # "0.01577100", // Close + # "148976.11427815", // Volume + # 1499644799999, // Close time + # "2434.19055334", // Quote asset volume + # 308, // Number of trades + # "1756.87402397", // Taker buy base asset volume + # "28.46694368", // Taker buy quote asset volume + # "17928899.62484339" // Ignore. + + candles_df = pd.DataFrame(candles).astype(float).iloc[:, :7] + + candles_df.columns = [ + "Time0", + "Open", + "High", + "Low", + "Close", + "Volume", + "Time1", + ] + candles_df.index = pd.to_datetime( + (candles_df["Time0"] + candles_df["Time1"]) / 2, unit="ms" + ) + + plot_candles( + candles_df, + f"{coin} from {candles_df.index[0].strftime('%Y/%m/%d')} to " + f"{candles_df.index[-1].strftime('%Y/%m/%d')}", + ) + + except Exception as e: + print(e, "\n") + + +def balance(coin: str): + """Get account holdings for asset + + Parameters + ---------- + coin: str + Coin to get holdings of + """ + client = Client(cfg.API_BINANCE_KEY, cfg.API_BINANCE_SECRET) + try: + current_balance = client.get_asset_balance(asset=coin) + if current_balance is None: + print("Check loaded coin") + return + print("") + amounts = [float(current_balance["free"]), float(current_balance["locked"])] + total = np.sum(amounts) + df = pd.DataFrame(amounts).apply(lambda x: str(float(x))) + df.columns = ["Amount"] + df.index = ["Free", "Locked"] + df["Percent"] = df.div(df.sum(axis=0), axis=1).round(3) + print(f"You currently have {total} coins and the breakdown is:") + print(tabulate(df, headers=df.columns, showindex=True, tablefmt="fancy_grid")) + print("") + return + except Exception as e: + print(e, "\n") + return diff --git a/gamestonk_terminal/cryptocurrency/binance_view.py b/gamestonk_terminal/cryptocurrency/binance_view.py new file mode 100644 index 00000000000..8ddbebee199 --- /dev/null +++ b/gamestonk_terminal/cryptocurrency/binance_view.py @@ -0,0 +1,78 @@ +"""Binance view functions""" +__docformat__ = "numpy" + +import numpy as np +import matplotlib.pyplot as plt +import mplfinance as mpf +import pandas as pd +from gamestonk_terminal.main_helper import plot_autoscale +from gamestonk_terminal.config_plot import PLOT_DPI +from gamestonk_terminal.feature_flags import USE_ION as ion + + +def plot_order_book(bids: np.array, asks: np.array, coin: str): + """ + Plots Bid/Ask + 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 + + Returns + ------- + + """ + _, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) + ax.plot(bids[:, 0], bids[:, 2], "g", label="bids") + ax.fill_between(bids[:, 0], bids[:, 2], color="g", alpha=0.4) + ax.plot(asks[:, 0], asks[:, 2], "r", label="asks") + ax.fill_between(asks[:, 0], asks[:, 2], color="r", alpha=0.4) + plt.grid(b=True, which="major", color="#666666", linestyle="-") + plt.minorticks_on() + plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2) + plt.legend(loc=0) + plt.xlabel("Price") + plt.ylabel("Size (Coins) ") + plt.title(f"Order Book for {coin}") + if ion: + plt.ion() + plt.show() + print("") + + +def plot_candles(candles_df: pd.DataFrame, title: str): + """ + Plot candle chart from dataframe + Parameters + ---------- + candles_df: pd.DataFrame + Dataframe containing time and OHLVC + title: str + title of graph + + Returns + ------- + + """ + mpf.plot( + candles_df, + type="candle", + volume=True, + title=f"{title}", + xrotation=20, + style="binance", + figratio=(10, 7), + figscale=1.10, + figsize=(plot_autoscale()), + update_width_config=dict( + candle_linewidth=1.0, candle_width=0.8, volume_linewidth=1.0 + ), + ) + if ion: + plt.ion() + plt.show() + print("") diff --git a/gamestonk_terminal/cryptocurrency/coinmarketcap_view.py b/gamestonk_terminal/cryptocurrency/coinmarketcap_view.py index a8820889d33..981b55ec092 100644 --- a/gamestonk_terminal/cryptocurrency/coinmarketcap_view.py +++ b/gamestonk_terminal/cryptocurrency/coinmarketcap_view.py @@ -6,7 +6,7 @@ from typing import List import pandas as pd from tabulate import tabulate from coinmarketcapapi import CoinMarketCapAPI -from gamestonk_terminal.config_terminal import COINMARKETCAP_KEY +import gamestonk_terminal.config_terminal as cfg from gamestonk_terminal.helper_funcs import parse_known_args_and_warn sort_options = ["Symbol", "CMC_Rank", "LastPrice", "DayPctChange", "MarketCap"] @@ -62,7 +62,7 @@ def get_cmc_top_n(other_args: List[str]): if not ns_parser: return - cmc = CoinMarketCapAPI(COINMARKETCAP_KEY) + cmc = CoinMarketCapAPI(cfg.API_CMC_KEY) ratings = cmc.cryptocurrency_listings_latest().data symbol, rank, price, pchange1d, mkt_cap = [], [], [], [], [] diff --git a/gamestonk_terminal/cryptocurrency/crypto_controller.py b/gamestonk_terminal/cryptocurrency/crypto_controller.py index 0361dcbbb23..958b345f5d5 100644 --- a/gamestonk_terminal/cryptocurrency/crypto_controller.py +++ b/gamestonk_terminal/cryptocurrency/crypto_controller.py @@ -1,3 +1,4 @@ +"""Cryptocurrency Controller""" __docformat__ = "numpy" import argparse @@ -7,13 +8,29 @@ from prompt_toolkit.completion import NestedCompleter from gamestonk_terminal import feature_flags as gtff from gamestonk_terminal.helper_funcs import get_flair from gamestonk_terminal.menu import session -from gamestonk_terminal.cryptocurrency import pycoingecko_view -from gamestonk_terminal.cryptocurrency import coinmarketcap_view as cmc_view +from gamestonk_terminal.cryptocurrency import ( + binance_model, + pycoingecko_view, + coinmarketcap_view as cmc_view, +) class CryptoController: - CHOICES = ["help", "q", "quit", "load", "view", "top"] + CHOICES = [ + "help", + "q", + "quit", + "load", + "view", + "top", + "trend", + "book", + "trades", + "candle", + "balance", + "select", + ] def __init__(self): """CONSTRUCTOR""" @@ -21,22 +38,31 @@ class CryptoController: self.crypto_parser = argparse.ArgumentParser(add_help=False, prog="crypto") self.crypto_parser.add_argument("cmd", choices=self.CHOICES) self.current_coin = None + self.current_currency = None self.current_df = pd.DataFrame() - @staticmethod - def print_help(current_coin): + def print_help(self): """Print help""" print("\nCryptocurrency:") print(" help show this menu again") print(" q quit this menu, and shows back to main menu") print(" quit quit to abandon program") - print(f"\nCurrent Coin: {current_coin}") + print(f"\nCurrent Coin: {self.current_coin}") print("") + print("Coingecko:") print(" load load cryptocurrency data") print(" view load and view cryptocurrency data") + print(" trend show top 7 trending coins") print("") + print("CoinMarketCap:") print(" top view top coins from coinmarketcap") print("") + print("Binance:") + print(" select select coin/currency to use") + print(" book show order book") + print(" candle get klines/candles and plot") + print(" balance show coin balance") + print("") def switch(self, an_input: str): """Process and dispatch input @@ -56,7 +82,7 @@ class CryptoController: def call_help(self, _): """Process Help command""" - self.print_help(self.current_coin) + self.print_help() def call_q(self, _): """Process Q command - quit the menu""" @@ -67,9 +93,11 @@ class CryptoController: return True def call_load(self, other_args): + """Process load command""" self.current_coin, self.current_df = pycoingecko_view.load(other_args) def call_view(self, other_args): + """Process view command""" if self.current_coin: pycoingecko_view.view(self.current_coin, self.current_df, other_args) @@ -77,13 +105,37 @@ class CryptoController: print("No coin selected. Use 'load' to load the coin you want to look at.") print("") + def call_trend(self, _): + """Process trend command""" + pycoingecko_view.trend() + def call_top(self, other_args): + """Process top command""" cmc_view.get_cmc_top_n(other_args) + def call_book(self, other_args): + """Process book command""" + binance_model.order_book(other_args, self.current_coin, self.current_currency) + + def call_candle(self, other_args): + """Process candle command""" + binance_model.show_candles(other_args, self.current_coin, self.current_currency) + + def call_balance(self, _): + """Process balance command""" + binance_model.balance(self.current_coin) + + def call_select(self, other_args): + """Process select command""" + self.current_coin, self.current_currency = binance_model.select_binance_coin( + other_args + ) + print("") + def menu(): crypto_controller = CryptoController() - crypto_controller.print_help(crypto_controller.current_coin) + crypto_controller.print_help() plt.close("all") while True: # Get input command from user diff --git a/gamestonk_terminal/cryptocurrency/crypto_helper.py b/gamestonk_terminal/cryptocurrency/crypto_helper.py deleted file mode 100644 index f895f264bd5..00000000000 --- a/gamestonk_terminal/cryptocurrency/crypto_helper.py +++ /dev/null @@ -1,15 +0,0 @@ -""" -Helper script that generates a dictionary to convert between symbols and IDs. -This allows the user to input (for example) "btc" instead of the expected "bitcoin" - -""" -from pycoingecko import CoinGeckoAPI - -cg = CoinGeckoAPI() -coins = cg.get_coins() -coin_symbol_to_id = {} -coin_ids = [] - -for coin in coins: - coin_symbol_to_id[coin["symbol"]] = coin["id"] - coin_ids.append(coin["id"]) diff --git a/gamestonk_terminal/cryptocurrency/pycoingecko_view.py b/gamestonk_terminal/cryptocurrency/pycoingecko_view.py index 3dd11a7663f..c4f6fca5f3c 100644 --- a/gamestonk_terminal/cryptocurrency/pycoingecko_view.py +++ b/gamestonk_terminal/cryptocurrency/pycoingecko_view.py @@ -6,12 +6,23 @@ from typing import List import pandas as pd from pandas.plotting import register_matplotlib_converters import matplotlib.pyplot as plt +from tabulate import tabulate from pycoingecko import CoinGeckoAPI from gamestonk_terminal.helper_funcs import parse_known_args_and_warn, plot_autoscale -from gamestonk_terminal.cryptocurrency.crypto_helper import coin_symbol_to_id, coin_ids from gamestonk_terminal.config_plot import PLOT_DPI register_matplotlib_converters() + +# Generate a list of valid coins to be checked against later +cg_api = CoinGeckoAPI() +coins = cg_api.get_coins() +coin_symbol_to_id = {} +coin_ids = [] + +for single_coin in coins: + coin_symbol_to_id[single_coin["symbol"]] = single_coin["id"] + coin_ids.append(single_coin["id"]) + # pylint: disable=inconsistent-return-statements @@ -135,3 +146,33 @@ def view(coin: str, prices: pd.DataFrame, other_args: List[str]): except Exception as e: print(e) print("") + + +def trend(): + """Prints top 7 coins from pycoingecko""" + + cg = CoinGeckoAPI() + trending = cg.get_search_trending()["coins"] + name, symbol, price, rank = [], [], [], [] + for coin in trending: + name.append(coin["item"]["name"]) + symbol.append(coin["item"]["symbol"]) + coin_id = coin["item"]["id"] + price.append(cg.get_price(coin_id, vs_currencies="USD")[coin_id]["usd"]) + rank.append(coin["item"]["market_cap_rank"]) + + df = pd.DataFrame() + df["name"] = name + df["symbol"] = symbol + df["last_price"] = price + df["market_cap_rank"] = rank + print( + tabulate( + df, + headers=df.columns, + floatfmt=".2f", + showindex=False, + tablefmt="fancy_grid", + ) + ) + print("") diff --git a/gamestonk_terminal/discovery/README.md b/gamestonk_terminal/discovery/README.md index 1159e591b56..b7b3ac0200a 100644 --- a/gamestonk_terminal/discovery/README.md +++ b/gamestonk_terminal/discovery/README.md @@ -2,6 +2,8 @@ This menu aims to discover new stocks, and the usage of the following commands along with an example will be exploited below. +* [ipo](#ipo) + * past and future IPOs [Finnhub] * [map](#map) * S&P500 index stocks map [Finviz] * [rtp_sectors](#rtp_sectors) @@ -42,6 +44,20 @@ This menu aims to discover new stocks, and the usage of the following commands a * dark pool tickers with growing activity [FINRA] +## ipo + +```shell +usage: ipo [-p PAST_DAYS] [-f FUTURE_DAYS] +``` + +Past and future IPOs. [Source: https://finnhub.io] + +* -p : Number of past days to look for IPOs. Default 0. +* -f : Number of future days to look for IPOs. Default 10. + +Captura de ecrã 2021-05-03, às 12 54 44 + + ## map ```shell diff --git a/gamestonk_terminal/discovery/disc_controller.py b/gamestonk_terminal/discovery/disc_controller.py index 855f2eeb0d4..07731915805 100644 --- a/gamestonk_terminal/discovery/disc_controller.py +++ b/gamestonk_terminal/discovery/disc_controller.py @@ -22,6 +22,7 @@ from gamestonk_terminal.discovery import ( yahoo_finance_view, marketbeat_view, finra_ats_view, + finnhub_view, ) @@ -33,6 +34,7 @@ class DiscoveryController: "help", "q", "quit", + "ipo", "map", "rtp_sectors", "gainers", @@ -72,6 +74,7 @@ class DiscoveryController: print(" q quit this menu, and shows back to main menu") print(" quit quit to abandon program") print("") + print(" ipo past and future IPOs [Finnhub]") print(" map S&P500 index stocks map [Finviz]") print(" rtp_sectors real-time performance sectors [Alpha Vantage]") print(" gainers show latest top gainers [Yahoo Finance]") @@ -135,6 +138,10 @@ class DiscoveryController: """Process Quit command - quit the program""" return True + def call_ipo(self, other_args: List[str]): + """Process ipo command""" + finnhub_view.ipo_calendar(other_args) + def call_map(self, other_args: List[str]): """Process map command""" finviz_view.map_sp500_view(other_args) diff --git a/gamestonk_terminal/discovery/finnhub_view.py b/gamestonk_terminal/discovery/finnhub_view.py new file mode 100644 index 00000000000..8fa058b54c3 --- /dev/null +++ b/gamestonk_terminal/discovery/finnhub_view.py @@ -0,0 +1,122 @@ +import argparse +from typing import List +from datetime import datetime, timedelta +import requests +import pandas as pd +from gamestonk_terminal import config_terminal as cfg +from gamestonk_terminal.helper_funcs import ( + parse_known_args_and_warn, + check_non_negative, +) + + +def get_ipo_calendar(from_date: str, to_date: str) -> pd.DataFrame: + """Get IPO calendar + + Parameters + ---------- + from_date : str + from date (%Y-%m-%d) to get IPO calendar + to_date : str + to date (%Y-%m-%d) to get IPO calendar + + Returns + ------- + pd.DataFrame + Get dataframe with economic calendar events + """ + response = requests.get( + f"https://finnhub.io/api/v1/calendar/ipo?from={from_date}&to={to_date}&token={cfg.API_FINNHUB_KEY}" + ) + 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", + } + return pd.DataFrame(d_data["ipoCalendar"]).rename( + columns=d_refactor_columns + ) + + return pd.DataFrame() + + +def ipo_calendar(other_args: List[str]): + """Past and future IPOs calendar [Finnhub] + + Parameters + ---------- + other_args : List[str] + Command line arguments to be processed with argparse + """ + parser = argparse.ArgumentParser( + add_help=False, + prog="ipo", + description=""" + Past and future IPOs. [Source: https://finnhub.io] + """, + ) + parser.add_argument( + "-p", + "--past", + action="store", + dest="past_days", + type=check_non_negative, + default=0, + help="Number of past days to look for IPOs.", + ) + parser.add_argument( + "-f", + "--future", + action="store", + dest="future_days", + type=check_non_negative, + default=10, + help="Number of future days to look for IPOs.", + ) + + try: + if other_args: + if "-" not in other_args[0]: + other_args.insert(0, "-f") + + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + + today = datetime.now() + + if ns_parser.future_days > 0: + future_date = today + timedelta(days=ns_parser.future_days) + + df_future_ipo = get_ipo_calendar( + today.strftime("%Y-%m-%d"), future_date.strftime("%Y-%m-%d") + ).rename(columns={"Date": "Future"}) + + if df_future_ipo.empty: + print(f"No IPOs found for the next {ns_parser.future_days} days") + else: + print(df_future_ipo.to_string(index=False)) + print("") + + if ns_parser.past_days > 0: + past_date = today - timedelta(days=ns_parser.past_days) + + df_past_ipo = get_ipo_calendar( + past_date.strftime("%Y-%m-%d"), today.strftime("%Y-%m-%d") + ).rename(columns={"Date": "Past"}) + + if df_past_ipo.empty: + print(f"No IPOs found since the last {ns_parser.past_days} days") + else: + print(df_past_ipo.to_string(index=False)) + print("") + + except Exception as e: + print(e, "\n") diff --git a/gamestonk_terminal/due_diligence/README.md b/gamestonk_terminal/due_diligence/README.md index cb8854b78e5..7a88574392a 100644 --- a/gamestonk_terminal/due_diligence/README.md +++ b/gamestonk_terminal/due_diligence/README.md @@ -12,6 +12,8 @@ This menu aims to help in due-diligence of a pre-loaded stock, and the usage of * rating of the company from strong sell to strong buy [FMP] * [pt](#pt) * price targets over time [Business Insider] +* [rot](#rot) + * ratings over time [Finnhub] * [est](#est) * quarter and year analysts earnings estimates [Business Insider] * [ins](#ins) @@ -89,6 +91,18 @@ Prints price target from analysts. [Source: Business Insider] pt2 + +## rot + +```text +usage: rot +``` + +Plots ratings over time. [Source: Finnhub] + +![rot](https://user-images.githubusercontent.com/25267873/116864981-ad282980-ac00-11eb-9e86-7163782a8ef6.png) + + ## est ```text diff --git a/gamestonk_terminal/due_diligence/dd_controller.py b/gamestonk_terminal/due_diligence/dd_controller.py index 27efe9eb880..f24ddcad017 100644 --- a/gamestonk_terminal/due_diligence/dd_controller.py +++ b/gamestonk_terminal/due_diligence/dd_controller.py @@ -15,6 +15,7 @@ from gamestonk_terminal.due_diligence import reddit_view as r_view from gamestonk_terminal.due_diligence import news_view from gamestonk_terminal.due_diligence import finra_view from gamestonk_terminal.due_diligence import sec_view +from gamestonk_terminal.due_diligence import finnhub_view from gamestonk_terminal import feature_flags as gtff from gamestonk_terminal.helper_funcs import get_flair from gamestonk_terminal.menu import session @@ -32,6 +33,7 @@ class DueDiligenceController: "short", "rating", "pt", + "rot", "est", "ins", "insider", @@ -91,6 +93,9 @@ class DueDiligenceController: " rating rating of the company from strong sell to strong buy [FMP]" ) print(" pt price targets over time [Business Insider]") + print( + " rot rating over timefrom strong sell to strong buy [Finnhub]" + ) print( " est quarter and year analysts earnings estimates [Business Insider]" ) @@ -155,6 +160,10 @@ class DueDiligenceController: other_args, self.stock, self.ticker, self.start, self.interval ) + def call_rot(self, other_args: List[str]): + """Process rot command""" + finnhub_view.rating_over_time(other_args, self.ticker) + def call_est(self, other_args: List[str]): """Process est command""" bi_view.estimates(other_args, self.ticker) diff --git a/gamestonk_terminal/due_diligence/finnhub_view.py b/gamestonk_terminal/due_diligence/finnhub_view.py new file mode 100644 index 00000000000..112032ea462 --- /dev/null +++ b/gamestonk_terminal/due_diligence/finnhub_view.py @@ -0,0 +1,108 @@ +import argparse +from typing import List +import requests +import pandas as pd +from matplotlib import pyplot as plt +from pandas.plotting import register_matplotlib_converters +from gamestonk_terminal import config_terminal as cfg +from gamestonk_terminal.helper_funcs import ( + parse_known_args_and_warn, + plot_autoscale, +) +from gamestonk_terminal.config_plot import PLOT_DPI +from gamestonk_terminal import feature_flags as gtff + +register_matplotlib_converters() + + +def get_rating_over_time(ticker: str) -> pd.DataFrame: + """Get rating over time + + Parameters + ---------- + ticker : str + Ticker to get ratings from + + Returns + ------- + pd.DataFrame + Get dataframe with economic calendar events + """ + response = requests.get( + f"https://finnhub.io/api/v1/stock/recommendation?symbol={ticker}&token={cfg.API_FINNHUB_KEY}" + ) + if response.status_code == 200: + return pd.DataFrame(response.json()) + + return pd.DataFrame() + + +def plot_rating_over_time(rot: pd.DataFrame, ticker: str): + """Plot rating over time + + Parameters + ---------- + rot : pd.DataFrame + Rating over time + ticker : str + Ticker associated with ratings + + """ + plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI) + + rot.sort_values("period", inplace=True) + plt.plot(pd.to_datetime(rot["period"]), rot["strongBuy"], c="green", lw=3) + plt.plot(pd.to_datetime(rot["period"]), rot["buy"], c="lightgreen", lw=3) + plt.plot(pd.to_datetime(rot["period"]), rot["hold"], c="grey", lw=3) + plt.plot(pd.to_datetime(rot["period"]), rot["sell"], c="pink", lw=3) + plt.plot(pd.to_datetime(rot["period"]), rot["strongSell"], c="red", lw=3) + plt.xlim( + pd.to_datetime(rot["period"].values[0]), + pd.to_datetime(rot["period"].values[-1]), + ) + plt.grid() + plt.title(f"{ticker}'s ratings over time") + plt.xlabel("Time") + plt.ylabel("Rating") + plt.legend(["Strong Buy", "Buy", "Hold", "Sell", "Strong Sell"]) + + if gtff.USE_ION: + plt.ion() + + plt.show() + + +def rating_over_time(other_args: List[str], ticker: str): + """Rating over time + + Parameters + ---------- + other_args : List[str] + Command line arguments to be processed with argparse + ticker : str + Ticker to get ratings from + """ + parser = argparse.ArgumentParser( + add_help=False, + prog="rot", + description=""" + Rating over time. [Source: https://finnhub.io] + """, + ) + + try: + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + + df_rot = get_rating_over_time(ticker) + + if df_rot.empty: + print("No ratings over time found\n") + return + + plot_rating_over_time(df_rot, ticker) + print("") + + except Exception as e: + print(e, "\n") diff --git a/gamestonk_terminal/econ/README.md b/gamestonk_terminal/econ/README.md index 1e6bb26f4b9..8bd5054abed 100644 --- a/gamestonk_terminal/econ/README.md +++ b/gamestonk_terminal/econ/README.md @@ -1,7 +1,9 @@ -# ECONONMY +# ECONOMY This menu aims to assess economy data, and the usage of the following commands along with an example will be exploited below. +* [events](#events) + * economic impact events [Finnhub] * [fred](#fred) * display customized FRED from https://fred.stlouisfed.org * [vixcls](#vixcls) @@ -28,6 +30,20 @@ This menu aims to assess economy data, and the usage of the following commands a * Canada / U.S. Foreign Exchange Rate (CAD per 1 USD) +## events +```text +usage: events [-c {NZ,AU,ERL,CA,EU,US,JP,CN,GB,CH}] [-i {low,medium,high,all}] [-n NUM] +``` + +Output economy impact calendar impact events. [Source: https://finnhub.io] + +* -c : Country from where to get economy calendar impact events, between NZ,AU,ERL,CA,EU,US,JP,CN,GB,CH. Default: US. +* -i : Impact of the economy event, between low,medium,high,all. Default: all. +* -n : Number economy calendar impact events to display. Default: 10. + +Captura de ecrã 2021-05-02, às 19 51 47 + + ## fred ```text usage: fred [-i SERIES_ID] [-s START_DATE] [-t] diff --git a/gamestonk_terminal/econ/econ_controller.py b/gamestonk_terminal/econ/econ_controller.py index 2bdaf1e8727..1b47f16d6b1 100644 --- a/gamestonk_terminal/econ/econ_controller.py +++ b/gamestonk_terminal/econ/econ_controller.py @@ -9,6 +9,7 @@ from gamestonk_terminal import feature_flags as gtff from gamestonk_terminal.helper_funcs import get_flair from gamestonk_terminal.menu import session from gamestonk_terminal.econ import fred_view +from gamestonk_terminal.econ import finnhub_view class EconController: @@ -19,6 +20,7 @@ class EconController: "help", "q", "quit", + "events", "fred", "vixcls", "gdp", @@ -50,6 +52,7 @@ class EconController: print(" q quit this menu, and shows back to main menu") print(" quit quit to abandon program") print(" ") + print(" events economic impact events [Finnhub]") print( " fred display customized FRED data from https://fred.stlouisfed.org" ) @@ -95,6 +98,10 @@ class EconController: """Process Quit command - quit the program""" return True + def call_events(self, other_args: List[str]): + """Process events command""" + finnhub_view.economy_calendar_events(other_args) + def call_fred(self, other_args: List[str]): """Process fred command""" fred_view.display_fred(other_args, "") diff --git a/gamestonk_terminal/econ/finnhub_view.py b/gamestonk_terminal/econ/finnhub_view.py new file mode 100644 index 00000000000..2f4d45d6c32 --- /dev/null +++ b/gamestonk_terminal/econ/finnhub_view.py @@ -0,0 +1,131 @@ +import argparse +from typing import List +import requests +import pandas as pd +from gamestonk_terminal import config_terminal as cfg +from gamestonk_terminal.helper_funcs import ( + parse_known_args_and_warn, + check_positive, +) + + +def get_economy_calendar_events() -> pd.DataFrame: + """Get economic calendar events + + Returns + ------- + pd.DataFrame + Get dataframe with economic calendar events + """ + response = requests.get( + f"https://finnhub.io/api/v1/calendar/economic?token={cfg.API_FINNHUB_KEY}" + ) + if response.status_code == 200: + d_data = response.json() + if "economicCalendar" in d_data: + return pd.DataFrame(d_data["economicCalendar"]) + + return pd.DataFrame() + + +def economy_calendar_events(other_args: List[str]): + """Output economy calendar impact events + + Parameters + ---------- + other_args : List[str] + Command line arguments to be processed with argparse + """ + parser = argparse.ArgumentParser( + add_help=False, + prog="events", + description=""" + Output economy impact calendar impact events. [Source: https://finnhub.io] + """, + ) + parser.add_argument( + "-c", + "--country", + action="store", + dest="country", + type=str, + default="US", + choices=["NZ", "AU", "ERL", "CA", "EU", "US", "JP", "CN", "GB", "CH"], + help="Country from where to get economy calendar impact events", + ) + parser.add_argument( + "-n", + "--num", + action="store", + dest="num", + type=check_positive, + default=10, + help="Number economy calendar impact events to display", + ) + parser.add_argument( + "-i", + "--impact", + action="store", + dest="impact", + type=str, + default="all", + choices=["low", "medium", "high", "all"], + help="Impact of the economy event", + ) + + try: + if other_args: + if "-" not in other_args[0]: + other_args.insert(0, "-c") + + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + + df_events = get_economy_calendar_events() + + if df_events.empty: + print("No latest economy calendar events found\n") + return + + df_econ_calendar = df_events[ + df_events["country"] == ns_parser.country + ].sort_values("time", ascending=False) + + if df_econ_calendar.empty: + print("No latet economy calendar events found in the specified country\n") + return + + if ns_parser.impact != "all": + df_econ_calendar = df_econ_calendar[ + df_econ_calendar["impact"] == ns_parser.impact + ] + + if df_econ_calendar.empty: + print( + "No latet economy calendar events found in the specified country with this impact\n" + ) + return + + df_econ_calendar = df_econ_calendar.fillna("---").head(n=ns_parser.num) + + d_econ_calendar_map = { + "actual": "Actual release", + "prev": "Previous release", + "country": "Country", + "unit": "Unit", + "estimate": "Estimate", + "event": "Event", + "impact": "Impact Level", + "time": "Release time", + } + + df_econ_calendar = df_econ_calendar[ + ["time", "event", "impact", "prev", "estimate", "actual", "unit"] + ].rename(columns=d_econ_calendar_map) + + print(df_econ_calendar.to_string(index=False)) + print("") + + except Exception as e: + print(e, "\n") diff --git a/gamestonk_terminal/econ/fred_view.py b/gamestonk_terminal/econ/fred_view.py index 59ccd2c2682..88ba23b42e8 100644 --- a/gamestonk_terminal/econ/fred_view.py +++ b/gamestonk_terminal/econ/fred_view.py @@ -58,7 +58,7 @@ def display_fred(other_args: List[str], choice: str): "-i", "--id", dest="series_id", - required=bool(choice), + required=not bool(choice), type=str, default=choice.upper(), help="FRED Series ID from https://fred.stlouisfed.org", diff --git a/gamestonk_terminal/main_helper.py b/gamestonk_terminal/main_helper.py index 824e14918b2..30ef54f0af0 100644 --- a/gamestonk_terminal/main_helper.py +++ b/gamestonk_terminal/main_helper.py @@ -55,11 +55,13 @@ def print_help(s_ticker, s_start, s_interval, b_is_market_open): " > scr screener stocks, \t\t e.g. overview/performance, using preset filters" ) print(" > mill papermill menu, \t\t menu to generate notebook reports") - print(" > econ economic data, \t\t from: FRED, VIX") + print(" > econ economic data, \t\t e.g.: FRED, events") print( " > pa portfolio analysis, \t\t supports: robinhood, alpaca, ally " ) - print(" > crypto cryptocurrencies, \t\t uses coingecko api") + print( + " > crypto cryptocurrencies, \t\t from: coingecko, coinmarketcap, binance" + ) print( " > po portfolio optimization, \t optimal portfolio weights from pyportfolioopt" ) diff --git a/gamestonk_terminal/technical_analysis/README.md b/gamestonk_terminal/technical_analysis/README.md index 0e5c511d850..aa5ca8a0fe6 100644 --- a/gamestonk_terminal/technical_analysis/README.md +++ b/gamestonk_terminal/technical_analysis/README.md @@ -8,6 +8,8 @@ This menu aims to perform a technical analysis on a pre-loaded ticker chart, and - technical summary report [FinBrain API] * [recom](#recom) - recommendation based on Technical Indicators [Tradingview API] + * [pr](#pr) + - pattern recognition [Finnhub] [OVERLAP](#OVERLAP) * [ema](#ema) @@ -79,6 +81,19 @@ Print tradingview recommendation based on technical indicators. [Source: https:/ Captura de ecrã 2021-03-31, às 00 14 41 +## pr +``` +usage: pr [-r {1,5,15,30,60,D,W,M}] +``` +Display pattern recognition signals on the data. [Source: https://finnhub.io] + +* -r : Plot resolution to look for pattern signals. Default 'D'. + +![pr](https://user-images.githubusercontent.com/25267873/116812520-06338700-ab47-11eb-8db4-8f5e940507ed.png) + +Captura de ecrã 2021-05-02, às 02 27 10 + + # OVERLAP ## ema diff --git a/gamestonk_terminal/technical_analysis/finnhub_view.py b/gamestonk_terminal/technical_analysis/finnhub_view.py new file mode 100644 index 00000000000..67db2b88951 --- /dev/null +++ b/gamestonk_terminal/technical_analysis/finnhub_view.py @@ -0,0 +1,185 @@ +import argparse +from typing import List +import math +from datetime import datetime +import requests +import yfinance as yf +import mplfinance as mpf +import pandas as pd +from gamestonk_terminal import config_terminal as cfg +from gamestonk_terminal.helper_funcs import ( + parse_known_args_and_warn, + plot_autoscale, +) + + +def get_pattern_recognition(ticker: str, resolution: str) -> pd.DataFrame: + """Get pattern recognition data + + Parameters + ---------- + ticker : str + Ticker to get pattern recognition data + resolution : str + Resolution of data to get pattern recognition from + + Returns + ------- + pd.DataFrame + Get datapoints corresponding to pattern signal data + """ + response = requests.get( + f"https://finnhub.io/api/v1/scan/pattern?symbol={ticker}&resolution={resolution}&token={cfg.API_FINNHUB_KEY}" + ) + if response.status_code == 200: + d_data = response.json() + if "points" in d_data: + return pd.DataFrame(d_data["points"]).T + + return pd.DataFrame() + + +def plot_pattern_recognition(ticker: str, pattern: pd.DataFrame): + """Plot pattern recognition signal + + Parameters + ---------- + ticker : str + Ticker to display pattern recognition on top of the data + pattern : pd.DataFrame + Pattern recognition signal data + """ + + l_segments = list() + for i in pattern: + a_part = ("", "") + if "aprice" in pattern[i]: + if pattern[i]["aprice"] != 0 and not math.isnan(pattern[i]["aprice"]): + a_part = ( + datetime.utcfromtimestamp(pattern[i]["atime"]).strftime("%Y-%m-%d"), + pattern[i]["aprice"], + ) + + b_part = ("", "") + if "bprice" in pattern[i]: + if pattern[i]["bprice"] != 0 and not math.isnan(pattern[i]["bprice"]): + b_part = ( + datetime.utcfromtimestamp(pattern[i]["btime"]).strftime("%Y-%m-%d"), + pattern[i]["bprice"], + ) + + c_part = ("", "") + if "cprice" in pattern[i]: + if pattern[i]["cprice"] != 0 and not math.isnan(pattern[i]["cprice"]): + c_part = ( + datetime.utcfromtimestamp(pattern[i]["ctime"]).strftime("%Y-%m-%d"), + pattern[i]["cprice"], + ) + + d_part = ("", "") + if "dprice" in pattern[i]: + if pattern[i]["dprice"] != 0 and not math.isnan(pattern[i]["dprice"]): + d_part = ( + datetime.utcfromtimestamp(pattern[i]["dtime"]).strftime("%Y-%m-%d"), + pattern[i]["dprice"], + ) + + segment = (a_part, b_part, c_part, d_part) + + l_segment = list(segment) + while ("", "") in l_segment: + l_segment.remove(("", "")) + segm = tuple(l_segment) + + l_segments.append(segm) + + start_time = 999999999999 + for i in pattern: + if pattern[i]["atime"] < start_time: + start_time = pattern[i]["atime"] + + df_stock = yf.download( + ticker, + start=datetime.utcfromtimestamp(start_time).strftime("%Y-%m-%d"), + progress=False, + ) + + df_stock["date_id"] = (df_stock.index.date - df_stock.index.date.min()).astype( + "timedelta64[D]" + ) + df_stock["date_id"] = df_stock["date_id"].dt.days + 1 + + df_stock["OC_High"] = df_stock[["Open", "Close"]].max(axis=1) + df_stock["OC_Low"] = df_stock[["Open", "Close"]].min(axis=1) + + mc = mpf.make_marketcolors( + up="green", down="red", edge="black", wick="black", volume="in", ohlc="i" + ) + + s = mpf.make_mpf_style(marketcolors=mc, gridstyle=":", y_on_right=False) + + mpf.plot( + df_stock, + type="candle", + volume=False, + title=f"\n{ticker}", + alines=l_segments, + xrotation=10, + style=s, + figratio=(10, 7), + figscale=1.10, + figsize=plot_autoscale(), + update_width_config=dict( + candle_linewidth=1.0, candle_width=0.8, volume_linewidth=1.0 + ), + ) + + for ix in range(len(pattern.columns)): + print(f"From {l_segments[ix][0][0]} to {l_segments[ix][-1][0]}") + print(f"Pattern: {pattern[0]['patternname']} ({pattern[0]['patterntype']})") + print("") + + +def pattern_recognition_view(other_args: List[str], ticker: str): + """Display pattern recognition signals on the data + + Parameters + ---------- + other_args : List[str] + Command line arguments to be processed with argparse + ticker : str + Ticker to display pattern recognition on top of the data + """ + parser = argparse.ArgumentParser( + add_help=False, + prog="pr", + description=""" + Display pattern recognition signals on the data. [Source: https://finnhub.io] + """, + ) + parser.add_argument( + "-r", + "--resolution", + action="store", + dest="resolution", + type=str, + default="D", + choices=["1", "5", "15", "30", "60", "D", "W", "M"], + help="Plot resolution to look for pattern signals", + ) + + try: + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + + df_pattern = get_pattern_recognition(ticker, ns_parser.resolution) + + if df_pattern.empty: + print("No pattern identified in this data", "\n") + return + + plot_pattern_recognition(ticker, df_pattern) + + except Exception as e: + print(e, "\n") diff --git a/gamestonk_terminal/technical_analysis/ta_controller.py b/gamestonk_terminal/technical_analysis/ta_controller.py index 620e13ab55f..2d48fced7d9 100644 --- a/gamestonk_terminal/technical_analysis/ta_controller.py +++ b/gamestonk_terminal/technical_analysis/ta_controller.py @@ -20,6 +20,7 @@ from gamestonk_terminal.technical_analysis import volume as ta_volume from gamestonk_terminal.technical_analysis import finbrain_view from gamestonk_terminal.technical_analysis import tradingview_view from gamestonk_terminal.technical_analysis import finviz_view +from gamestonk_terminal.technical_analysis import finnhub_view class TechnicalAnalysisController: @@ -33,6 +34,7 @@ class TechnicalAnalysisController: "view", "summary", "recom", + "pr", "ema", "sma", "vwap", @@ -88,6 +90,8 @@ class TechnicalAnalysisController: print( " recom recommendation based on Technical Indicators [Tradingview API]" ) + print(" pr pattern recognition [Finnhub]") + print("") print("overlap:") print(" ema exponential moving average") print(" sma simple moving average") @@ -157,6 +161,10 @@ class TechnicalAnalysisController: """Process recom command""" tradingview_view.print_recommendation(other_args, self.ticker) + def call_pr(self, other_args: List[str]): + """Process pr command""" + finnhub_view.pattern_recognition_view(other_args, self.ticker) + # OVERLAP def call_ema(self, other_args: List[str]): """Process ema command""" diff --git a/images/gst_logo_lockup_rGreen_with_letters.png b/images/gst_logo_lockup_rGreen_with_letters.png new file mode 100644 index 00000000000..a3e6d6782f6 Binary files /dev/null and b/images/gst_logo_lockup_rGreen_with_letters.png differ diff --git a/poetry.lock b/poetry.lock index 3b481c673f8..6b62906f552 100644 --- a/poetry.lock +++ b/poetry.lock @@ -436,6 +436,23 @@ category = "main" optional = false python-versions = ">=3.6, <3.7" +[[package]] +name = "dateparser" +version = "1.0.0" +description = "Date parsing library designed to parse dates from HTML pages" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +python-dateutil = "*" +pytz = "*" +regex = "!=2019.02.19" +tzlocal = "*" + +[package.extras] +calendars = ["convertdate", "hijri-converter", "convertdate"] + [[package]] name = "datetime" version = "4.3" @@ -712,7 +729,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "gdown" -version = "3.12.2" +version = "3.13.0" description = "Google Drive direct download of big files." category = "main" optional = true @@ -720,7 +737,7 @@ python-versions = "*" [package.dependencies] filelock = "*" -requests = {version = "*", extras = ["socks"]} +requests = {version = ">=2.12.0", extras = ["socks"]} six = "*" tqdm = "*" @@ -2009,7 +2026,7 @@ python-versions = "*" [[package]] name = "pyobjc-core" -version = "7.1" +version = "7.2" description = "Python<->ObjC Interoperability Module" category = "main" optional = false @@ -2017,14 +2034,14 @@ python-versions = ">=3.6" [[package]] name = "pyobjc-framework-cocoa" -version = "7.1" +version = "7.2" description = "Wrappers for the Cocoa frameworks on macOS" category = "main" optional = false python-versions = ">=3.6" [package.dependencies] -pyobjc-core = ">=7.1" +pyobjc-core = ">=7.2" [[package]] name = "pyotp" @@ -2120,6 +2137,22 @@ toml = "*" [package.extras] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] +[[package]] +name = "python-binance" +version = "1.0.1" +description = "Binance REST API python implementation" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +aiohttp = "*" +dateparser = "*" +requests = "*" +six = "*" +ujson = "*" +websockets = "*" + [[package]] name = "python-coinmarketcap" version = "0.2" @@ -2173,7 +2206,7 @@ python-versions = "*" [[package]] name = "pyupgrade" -version = "2.13.0" +version = "2.14.0" description = "A tool to automatically upgrade syntax for newer versions." category = "dev" optional = false @@ -2232,7 +2265,7 @@ scipy = ">=0.13.2" [[package]] name = "qtconsole" -version = "5.0.3" +version = "5.1.0" description = "Jupyter Qt console" category = "main" optional = false @@ -2863,6 +2896,25 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "tzlocal" +version = "2.1" +description = "tzinfo object for the local timezone" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +pytz = "*" + +[[package]] +name = "ujson" +version = "4.0.2" +description = "Ultra fast JSON encoder and decoder for Python" +category = "main" +optional = false +python-versions = ">=3.6" + [[package]] name = "update-checker" version = "0.18.0" @@ -3036,7 +3088,7 @@ prediction = ["fbprophet", "tensorflow", "pmdarima", "transformers", "flair", "t [metadata] lock-version = "1.1" python-versions = "^3.6.8" -content-hash = "51e9191a579c58d229a6a32b419f3f8d891d2e7ce12decb6f74a283fb46a851a" +content-hash = "9f2c8b046494863b5a83e395b8a79edbca42fd2b61b5e977877e08aedc53e165" [metadata.files] absl-py = [ @@ -3329,6 +3381,10 @@ dataclasses = [ {file = "dataclasses-0.8-py3-none-any.whl", hash = "sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf"}, {file = "dataclasses-0.8.tar.gz", hash = "sha256:8479067f342acf957dc82ec415d355ab5edb7e7646b90dc6e2fd1d96ad084c97"}, ] +dateparser = [ + {file = "dateparser-1.0.0-py2.py3-none-any.whl", hash = "sha256:17202df32c7a36e773136ff353aa3767e987f8b3e27374c39fd21a30a803d6f8"}, + {file = "dateparser-1.0.0.tar.gz", hash = "sha256:159cc4e01a593706a15cd4e269a0b3345edf3aef8bf9278a57dac8adf5bf1e4a"}, +] datetime = [ {file = "DateTime-4.3-py2.py3-none-any.whl", hash = "sha256:371dba07417b929a4fa685c2f7a3eaa6a62d60c02947831f97d4df9a9e70dfd0"}, {file = "DateTime-4.3.tar.gz", hash = "sha256:5cef605bab8259ff61281762cdf3290e459fbf0b4719951d5fab967d5f2ea0ea"}, @@ -3458,7 +3514,7 @@ gast = [ {file = "gast-0.3.3.tar.gz", hash = "sha256:b881ef288a49aa81440d2c5eb8aeefd4c2bb8993d5f50edae7413a85bfdb3b57"}, ] gdown = [ - {file = "gdown-3.12.2.tar.gz", hash = "sha256:4b3a1301e57bfd8dce939bf25ef8fbb4b23967fd0f878eede328bdcc41386bac"}, + {file = "gdown-3.13.0.tar.gz", hash = "sha256:d5f9389539673875712beba4936c4ace95d24324953c6f0408a858c534c0bf21"}, ] gensim = [ {file = "gensim-3.8.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:61eed1d6b5fbe6dda0586ea447ebc2dc7890a7f70c2ed953d5abc3fe3cfb94bb"}, @@ -4335,20 +4391,20 @@ pymeeus = [ {file = "PyMeeus-0.5.11.tar.gz", hash = "sha256:bb9d670818d8b0594317b48a7dadea02a0594e5344263bf2054e1a011c8fed55"}, ] pyobjc-core = [ - {file = "pyobjc-core-7.1.tar.gz", hash = "sha256:a0616d5d816b4471f8f782c3a9a8923d2cc85014d88ad4f7fec694be9e6ea349"}, - {file = "pyobjc_core-7.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9fb45c9916f2a03ecd6b9ecde4c35d1d0f1a590ae2ea2372f9d9a360226ac1d"}, - {file = "pyobjc_core-7.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fff8e87358c6195a2937004f279050cce3d4c02cd77acd73c5ad367307def855"}, - {file = "pyobjc_core-7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:afb38efd3f2960eb49eb78552d465cfd025a9d6efa06cd4cd8694dafbe7c6e06"}, - {file = "pyobjc_core-7.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7cb329c4119044fe83bcb3c5d4794d636c706ff0cb7c1c77d36ef5c373100082"}, - {file = "pyobjc_core-7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7913d7b20217c294900537faf58e5cc15942ed7af277bf05db25667d18255114"}, + {file = "pyobjc-core-7.2.tar.gz", hash = "sha256:9e9ec482d80ea030cdb1613d05a247f31eedabe6666d884d42dd890cc5fb0e05"}, + {file = "pyobjc_core-7.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:94b4d9de9d228db52dd35012096d63bdf8c1ace58ea3be1d5f6f39313cd502f2"}, + {file = "pyobjc_core-7.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:971cbd7189ae1aa03ef0d16124aa5bcd053779e0e6b6011a41c3dbd5b4ea7e88"}, + {file = "pyobjc_core-7.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9d93b20394008373d6d2856d49aaff26f4b97ff42d924a14516c8a82313ec8c0"}, + {file = "pyobjc_core-7.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:860183540d1be792c26426018139ac8ba75e85f675c59ba080ccdc52d8e74c7a"}, + {file = "pyobjc_core-7.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ffe61d3c2a404354daf2d895e34e38c5044453353581b3c396bf5365de26250c"}, ] pyobjc-framework-cocoa = [ - {file = "pyobjc-framework-Cocoa-7.1.tar.gz", hash = "sha256:67966152b3d38a0225176fceca2e9f56d849c8e7445548da09a00cb13155ec3e"}, - {file = "pyobjc_framework_Cocoa-7.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:bef77eafaac5eaf1d91d479d5483fd02216caa3edc27e8f5adc9af0b3fecdac3"}, - {file = "pyobjc_framework_Cocoa-7.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b2ea3582c456827dc20e648c905fdbcf8d3dfae89434f981e9b761cd07262049"}, - {file = "pyobjc_framework_Cocoa-7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a4050f2d776f40c2409a151c6f7896420e936934b3bdbfabedf91509637ed9b"}, - {file = "pyobjc_framework_Cocoa-7.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3f68f022f1f6d5985c418e10c6608c562fcf4bfe3714ec64fd10ce3dc6221bd4"}, - {file = "pyobjc_framework_Cocoa-7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ecfefd4c48dae42275c18679c69f6f2fff970e711097515a0a8732fc10194018"}, + {file = "pyobjc-framework-Cocoa-7.2.tar.gz", hash = "sha256:c8b23f03dc3f4436d36c0fd006a8a084835c4f6015187df7c3aa5de8ecd5c653"}, + {file = "pyobjc_framework_Cocoa-7.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8e5dd5daa0096755937ec24c345a4b07c3fa131a457f99e0fdeeb01979178ec7"}, + {file = "pyobjc_framework_Cocoa-7.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:828d183947fc7746953fd0c9b1092cc423745ba0b49719e7b7d1e1614aaa20ec"}, + {file = "pyobjc_framework_Cocoa-7.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7e4c6d7baa0c2ab5ea5efb8836ad0b3b3976cffcfc6195c1f195e826c6eb5744"}, + {file = "pyobjc_framework_Cocoa-7.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c9a9d1d49cc5a810773c88d6de821e60c8cc41d01113cf1b9e7662938f5f7d66"}, + {file = "pyobjc_framework_Cocoa-7.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:506c2cd09f421eac92b9008a0142174c3d1d70ecd4b0e3fa2b924767995fd14e"}, ] pyotp = [ {file = "pyotp-2.6.0-py2.py3-none-any.whl", hash = "sha256:9d144de0f8a601d6869abe1409f4a3f75f097c37b50a36a3bf165810a6e23f28"}, @@ -4400,6 +4456,10 @@ pytest = [ {file = "pytest-6.2.3-py3-none-any.whl", hash = "sha256:6ad9c7bdf517a808242b998ac20063c41532a570d088d77eec1ee12b0b5574bc"}, {file = "pytest-6.2.3.tar.gz", hash = "sha256:671238a46e4df0f3498d1c3270e5deb9b32d25134c99b7d75370a68cfbe9b634"}, ] +python-binance = [ + {file = "python-binance-1.0.1.tar.gz", hash = "sha256:7987189651938bb81a4e2f86993a32c77c55255d9c33891efb1d2bca918b8142"}, + {file = "python_binance-1.0.1-py2.py3-none-any.whl", hash = "sha256:80dc4402837103024c44f0f57c5ac90c43e5ebe1da4f53104ebd4b9b788bd17d"}, +] python-coinmarketcap = [ {file = "python-coinmarketcap-0.2.tar.gz", hash = "sha256:b1e0e8b098fc910a5ec943a06774d5ee3b190e915123366e2f16238ef0a8a433"}, {file = "python_coinmarketcap-0.2-py2-none-any.whl", hash = "sha256:54c278154ee11ca78837fb73eae8c322a60af93dc0ce75b515fecf895d181cf5"}, @@ -4422,8 +4482,8 @@ pytz = [ {file = "pytz-2021.1.tar.gz", hash = "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da"}, ] pyupgrade = [ - {file = "pyupgrade-2.13.0-py2.py3-none-any.whl", hash = "sha256:1cb5a15573fc63824b952bf7dde5b96f9672a1179ec779ac2d159a845da743c8"}, - {file = "pyupgrade-2.13.0.tar.gz", hash = "sha256:dc6b580b7bc82f357acb354bbba03d8d69da51425ecd5993a2651059bca80518"}, + {file = "pyupgrade-2.14.0-py2.py3-none-any.whl", hash = "sha256:19bd23babd4c09171be66d393d83a53e5ae8d54c6534001c4b7ad92c4d560d69"}, + {file = "pyupgrade-2.14.0.tar.gz", hash = "sha256:34f5dfa57d8007f6cd455a0a423e06e96753b07e4711bac63e3d5c4f3af97cea"}, ] pywin32 = [ {file = "pywin32-300-cp35-cp35m-win32.whl", hash = "sha256:1c204a81daed2089e55d11eefa4826c05e604d27fe2be40b6bf8db7b6a39da63"}, @@ -4528,8 +4588,8 @@ qdldl = [ {file = "qdldl-0.1.5.post0.tar.gz", hash = "sha256:c392c7427651d8b226423c7aba4a0f2338a1f38a4bbdabac6bc4afd8bc934f06"}, ] qtconsole = [ - {file = "qtconsole-5.0.3-py3-none-any.whl", hash = "sha256:4a38053993ca2da058f76f8d75b3d8906efbf9183de516f92f222ac8e37d9614"}, - {file = "qtconsole-5.0.3.tar.gz", hash = "sha256:c091a35607d2a2432e004c4a112d241ce908086570cf68594176dd52ccaa212d"}, + {file = "qtconsole-5.1.0-py3-none-any.whl", hash = "sha256:3a2adecc43ff201a08972fb2179df22e7b3a08d71b9ed680f46ad1bfd4fb9132"}, + {file = "qtconsole-5.1.0.tar.gz", hash = "sha256:12c734494901658787339dea9bbd82f3dc0d5e394071377a1c77b4a0954d7d8b"}, ] qtpy = [ {file = "QtPy-1.9.0-py2.py3-none-any.whl", hash = "sha256:fa0b8363b363e89b2a6f49eddc162a04c0699ae95e109a6be3bb145a913190ea"}, @@ -5009,6 +5069,33 @@ typing-extensions = [ {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"}, {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, ] +tzlocal = [ + {file = "tzlocal-2.1-py2.py3-none-any.whl", hash = "sha256:e2cb6c6b5b604af38597403e9852872d7f534962ae2954c7f35efcb1ccacf4a4"}, + {file = "tzlocal-2.1.tar.gz", hash = "sha256:643c97c5294aedc737780a49d9df30889321cbe1204eac2c2ec6134035a92e44"}, +] +ujson = [ + {file = "ujson-4.0.2-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:e390df0dcc7897ffb98e17eae1f4c442c39c91814c298ad84d935a3c5c7a32fa"}, + {file = "ujson-4.0.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:84b1dca0d53b0a8d58835f72ea2894e4d6cf7a5dd8f520ab4cbd698c81e49737"}, + {file = "ujson-4.0.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:91396a585ba51f84dc71c8da60cdc86de6b60ba0272c389b6482020a1fac9394"}, + {file = "ujson-4.0.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:eb6b25a7670c7537a5998e695fa62ff13c7f9c33faf82927adf4daa460d5f62e"}, + {file = "ujson-4.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:f8aded54c2bc554ce20b397f72101737dd61ee7b81c771684a7dd7805e6cca0c"}, + {file = "ujson-4.0.2-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:30962467c36ff6de6161d784cd2a6aac1097f0128b522d6e9291678e34fb2b47"}, + {file = "ujson-4.0.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:fc51e545d65689c398161f07fd405104956ec27f22453de85898fa088b2cd4bb"}, + {file = "ujson-4.0.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e6e90330670c78e727d6637bb5a215d3e093d8e3570d439fd4922942f88da361"}, + {file = "ujson-4.0.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:5e1636b94c7f1f59a8ead4c8a7bab1b12cc52d4c21ababa295ffec56b445fd2a"}, + {file = "ujson-4.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:e2cadeb0ddc98e3963bea266cc5b884e5d77d73adf807f0bda9eca64d1c509d5"}, + {file = "ujson-4.0.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:a214ba5a21dad71a43c0f5aef917cd56a2d70bc974d845be211c66b6742a471c"}, + {file = "ujson-4.0.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:0190d26c0e990c17ad072ec8593647218fe1c675d11089cd3d1440175b568967"}, + {file = "ujson-4.0.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:f273a875c0b42c2a019c337631bc1907f6fdfbc84210cc0d1fff0e2019bbfaec"}, + {file = "ujson-4.0.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:d3a87888c40b5bfcf69b4030427cd666893e826e82cc8608d1ba8b4b5e04ea99"}, + {file = "ujson-4.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:7333e8bc45ea28c74ae26157eacaed5e5629dbada32e0103c23eb368f93af108"}, + {file = "ujson-4.0.2-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:b3a6dcc660220539aa718bcc9dbd6dedf2a01d19c875d1033f028f212e36d6bb"}, + {file = "ujson-4.0.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:0ea07fe57f9157118ca689e7f6db72759395b99121c0ff038d2e38649c626fb1"}, + {file = "ujson-4.0.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:4d6d061563470cac889c0a9fd367013a5dbd8efc36ad01ab3e67a57e56cad720"}, + {file = "ujson-4.0.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b5c70704962cf93ec6ea3271a47d952b75ae1980d6c56b8496cec2a722075939"}, + {file = "ujson-4.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:aad6d92f4d71e37ea70e966500f1951ecd065edca3a70d3861b37b176dd6702c"}, + {file = "ujson-4.0.2.tar.gz", hash = "sha256:c615a9e9e378a7383b756b7e7a73c38b22aeb8967a8bfbffd4741f7ffd043c4d"}, +] update-checker = [ {file = "update_checker-0.18.0-py3-none-any.whl", hash = "sha256:cbba64760a36fe2640d80d85306e8fe82b6816659190993b7bdabadee4d4bbfd"}, {file = "update_checker-0.18.0.tar.gz", hash = "sha256:6a2d45bb4ac585884a6b03f9eade9161cedd9e8111545141e9aa9058932acb13"}, diff --git a/pyproject.toml b/pyproject.toml index 65ac1931eb2..43efb105290 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,6 +63,7 @@ oandapyV20 = "^0.6.3" degiroapi = "^0.9.5" valinvest = "^0.0.2" bt = "^0.2.9" +python-binance = "^1.0.1" [tool.poetry.dev-dependencies] pytest = "^6.2.2" diff --git a/requirements-full.txt b/requirements-full.txt index a8c07a921e4..a3f79bc3a5d 100644 --- a/requirements-full.txt +++ b/requirements-full.txt @@ -149,6 +149,7 @@ pyportfolioopt==1.4.1; python_full_version >= "3.6.1" and python_full_version < pyrsistent==0.14.11 pysocks==1.7.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" pystan==2.19.1.1 +python-binance==1.0.1 python-coinmarketcap==0.2 python-dateutil==2.8.1; python_full_version >= "3.6.1" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6") and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0") and (python_version >= "2.7" and python_full_version < "3.0.0" and python_version < "4" or python_version >= "2.7" and python_version < "4" and python_full_version >= "3.3.0") python-dotenv==0.17.0; python_version >= "3" diff --git a/requirements-old.txt b/requirements-old.txt index 77b359de143..71b5b6d736b 100644 --- a/requirements-old.txt +++ b/requirements-old.txt @@ -94,6 +94,7 @@ pyparsing==2.4.7; python_full_version >= "3.6.0" and python_version >= "3.6" pyportfolioopt==1.4.1 pysocks==1.7.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" pystan==2.18 +python-binance==1.0.1 python-coinmarketcap==0.2 python-dateutil==2.8.1; python_full_version >= "3.6.1" and python_version >= "3.5" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6") and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0") and (python_version >= "2.7" and python_full_version < "3.0.0" and python_version < "4" or python_version >= "2.7" and python_version < "4" and python_full_version >= "3.3.0") pytest==6.2.2; python_version >= "3.6" diff --git a/requirements-win.txt b/requirements-win.txt index 1f658e78cc9..b12bd43282f 100644 --- a/requirements-win.txt +++ b/requirements-win.txt @@ -96,6 +96,7 @@ pyparsing==2.4.7; python_full_version >= "3.6.0" and python_version >= "3.6" pyportfolioopt==1.4.1 pysocks==1.7.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" pystan==2.19.1.1 +python-binance==1.0.1 python-coinmarketcap==0.2 python-dateutil==2.8.1; python_full_version >= "3.6.1" and python_version >= "3.5" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6") and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0") and (python_version >= "2.7" and python_full_version < "3.0.0" and python_version < "4" or python_version >= "2.7" and python_version < "4" and python_full_version >= "3.3.0") pytest==6.2.2; python_version >= "3.6" diff --git a/requirements.txt b/requirements.txt index 4a189ab8556..04cdb8207db 100644 --- a/requirements.txt +++ b/requirements.txt @@ -113,6 +113,7 @@ pyparsing==2.4.7; python_version >= "3.6" and python_full_version < "3.0.0" or p pyportfolioopt==1.4.1; python_full_version >= "3.6.1" and python_full_version < "4.0.0" pyrsistent==0.14.11 pysocks==1.7.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" +python-binance==1.0.1 python-coinmarketcap==0.2 python-dateutil==2.8.1; python_full_version >= "3.6.1" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6") and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0") python-dotenv==0.17.0; python_version >= "3"