From 7fe22c18909354c430ef943f9b996dc5de312b5a Mon Sep 17 00:00:00 2001 From: Colin Delahunty <72827203+colin99d@users.noreply.github.com> Date: Sun, 9 Oct 2022 14:30:49 +0200 Subject: [PATCH] Added back dashboards (sorry Didier) (#2751) --- .gitignore | 2 +- openbb_terminal/dashboards/voila/chains.ipynb | 177 ++++++++ .../dashboards/voila/correlation.ipynb | 207 +++++++++ openbb_terminal/dashboards/voila/crypto.ipynb | 160 +++++++ .../dashboards/voila/currencies.ipynb | 284 ++++++++++++ .../dashboards/voila/forecast.ipynb | 320 ++++++++++++++ .../dashboards/voila/futures.ipynb | 321 ++++++++++++++ .../dashboards/voila/shortdata.ipynb | 236 ++++++++++ openbb_terminal/dashboards/voila/stocks.ipynb | 404 ++++++++++++++++++ openbb_terminal/dashboards/voila/vsurf.ipynb | 162 +++++++ 10 files changed, 2272 insertions(+), 1 deletion(-) create mode 100644 openbb_terminal/dashboards/voila/chains.ipynb create mode 100644 openbb_terminal/dashboards/voila/correlation.ipynb create mode 100644 openbb_terminal/dashboards/voila/crypto.ipynb create mode 100644 openbb_terminal/dashboards/voila/currencies.ipynb create mode 100644 openbb_terminal/dashboards/voila/forecast.ipynb create mode 100644 openbb_terminal/dashboards/voila/futures.ipynb create mode 100644 openbb_terminal/dashboards/voila/shortdata.ipynb create mode 100644 openbb_terminal/dashboards/voila/stocks.ipynb create mode 100644 openbb_terminal/dashboards/voila/vsurf.ipynb diff --git a/.gitignore b/.gitignore index ec2ff54e88f..01c3bc92e52 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,7 @@ openbb_terminal/config_terminal.py openbb_terminal/feature_flags.py openbb_terminal/reports/stored/* !openbb_terminal/reports/stored/.gitkeep -!openbb_terminal/dashboards/*.ipynb +!openbb_terminal/dashboards/voila/*.ipynb !openbb_terminal/reports/*.ipynb openbb_terminal/portfolio/portfolios/* **/.ipynb_checkpoints diff --git a/openbb_terminal/dashboards/voila/chains.ipynb b/openbb_terminal/dashboards/voila/chains.ipynb new file mode 100644 index 00000000000..c17412479ee --- /dev/null +++ b/openbb_terminal/dashboards/voila/chains.ipynb @@ -0,0 +1,177 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "e9ad60bc", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import ipywidgets as widgets\n", + "import plotly.graph_objs as go\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, 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", + " autosize=False,\n", + " width=1000,\n", + " height=500,\n", + " title={\n", + " \"text\": title,\n", + " \"y\": 0.95,\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 = go.Figure()\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", + " plot = go.Scatter(\n", + " x=df[x],\n", + " y=df[y],\n", + " mode=\"lines\",\n", + " connectgaps=True,\n", + " name=expire,\n", + " )\n", + " fig.add_trace(plot)\n", + "\n", + " format_plotly(fig, x, y, ticker, expiry, inst)\n", + "\n", + " if os.environ.get(\"SERVER_SOFTWARE\", \"jupyter\").startswith(\"voila\"):\n", + " fig.show(config={\"showTips\": False}, renderer=\"notebook\")\n", + " else:\n", + " fig.show(config={\"showTips\": 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 = \"
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": "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/crypto.ipynb b/openbb_terminal/dashboards/voila/crypto.ipynb new file mode 100644 index 00000000000..7972cda6037 --- /dev/null +++ b/openbb_terminal/dashboards/voila/crypto.ipynb @@ -0,0 +1,160 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "6e198e40-0c33-45b3-a79b-2d9ee9eff7b9", + "metadata": { + "tags": [] + }, + "source": [ + "# Cryptocurrency exchange rates to USD ($)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "da655985-c9b7-492e-aefa-fbe3bc1865d5", + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "import os\n", + "\n", + "import ipywidgets as ipw\n", + "from IPython.display import display\n", + "import pandas as pd\n", + "\n", + "from openbb_terminal import api as openbb\n", + "\n", + "ipw.HTML(f\"\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dcbbf867-af50-4d06-ae5c-7c09647aa55d", + "metadata": {}, + "outputs": [], + "source": [ + "COIN_LIST = (\n", + " \"BTC,ETH,USDT,BNB,USDC,XRP,LUNA,ADA,\"\n", + " + \"SOL,AVAX,DOT,BUSD,DOGE,UST,SHIB,WBTC,\"\n", + " + \"MATIC,CRO,DAI,LTC,ATOM,NEAR,LINK,BCH,\"\n", + " + \"UNI,TRX,FTT,ETC,LEO,ALGO,XLM,MANA,\"\n", + " + \"BTCB,HBAR,EGLD,ICP,SAND,XMR,WAVES,VET,\"\n", + " + \"APE,FIL,FTM,AXS,THETA,KLAY,XTZ,RUNE\"\n", + ").split(\",\")\n", + "exchange_rates = {}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "deeeb80a-3675-44fb-9a1b-2325d8da4e7d", + "metadata": {}, + "outputs": [], + "source": [ + "def get_exchange_rate(coin: str, days: int = 2):\n", + " \"\"\"Get exchange rate for a cryptocurrency agains USD.\"\"\"\n", + " (_, _, _, _, current_df, _,) = openbb.crypto.load(\n", + " coin=coin,\n", + " source=\"cp\",\n", + " days=days,\n", + " vs=\"usd\",\n", + " interval=\"1day\",\n", + " should_load_ta_data=True,\n", + " )\n", + " price_color = \"neutral_color\"\n", + " if current_df[\"Close\"].iloc[-1] > current_df[\"Close\"].iloc[-2]:\n", + " price_color = \"up_color\"\n", + " elif current_df[\"Close\"].iloc[-1] < current_df[\"Close\"].iloc[-2]:\n", + " price_color = \"down_color\"\n", + " price = str(current_df[\"Close\"].iloc[-1])[:7]\n", + "\n", + " return price, price_color" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18acf5cb-6cbf-484d-8042-107c030098de", + "metadata": {}, + "outputs": [], + "source": [ + "def show_rates(btn):\n", + " \"\"\"Show exchange rates in grid box widget.\"\"\"\n", + " grid.children = ()\n", + " for coin in COIN_LIST:\n", + " if coin not in exchange_rates.keys():\n", + " exchange_rates[coin] = {\"price\": None, \"color\": None}\n", + " try:\n", + " price, price_color = get_exchange_rate(coin=coin)\n", + " except:\n", + " price, price_color = \"-------\", \"neutral_color\"\n", + " widgets = list(grid.children)\n", + " widgets.append(\n", + " ipw.HTML(\n", + " openbb.widgets.price_card(\n", + " ticker=coin,\n", + " price=price,\n", + " price_color=price_color,\n", + " )\n", + " )\n", + " )\n", + " grid.children = tuple(widgets)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cf2021e9-de8a-466e-b341-fcd6abda64b5", + "metadata": {}, + "outputs": [], + "source": [ + "update_rates = ipw.Button(description=\"Update rates\")\n", + "update_rates.on_click(show_rates)\n", + "header = ipw.HBox([update_rates])\n", + "\n", + "layout = ipw.Layout(grid_template_columns=\"1fr \" * 8)\n", + "grid = ipw.GridBox(\n", + " [],\n", + " layout=layout,\n", + ")\n", + "\n", + "display(header, grid)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "19dfcbfe-eaec-406e-bb3c-72d1b3538a6e", + "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" + }, + "voila": { + "theme": "dark" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/openbb_terminal/dashboards/voila/currencies.ipynb b/openbb_terminal/dashboards/voila/currencies.ipynb new file mode 100644 index 00000000000..dd3f5661365 --- /dev/null +++ b/openbb_terminal/dashboards/voila/currencies.ipynb @@ -0,0 +1,284 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "da655985-c9b7-492e-aefa-fbe3bc1865d5", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import sys\n", + "import time\n", + "from datetime import datetime, timedelta\n", + "from typing import Optional\n", + "\n", + "import ipywidgets as ipw\n", + "import matplotlib.pyplot as plt\n", + "from ipyflex import FlexLayout\n", + "import matplotlib_inline.backend_inline\n", + "from openbb_terminal.dashboards.widget_helpers import price_card, stylesheet\n", + "\n", + "from openbb_terminal import api as openbb\n", + "\n", + "%matplotlib widget\n", + "openbb.theme.applyMPLstyle()\n", + "ipw.HTML(f\"\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5905453-4de6-4f5f-8012-9acdfca50987", + "metadata": {}, + "outputs": [], + "source": [ + "def get_exchange_rate(currency_pair: str) -> str:\n", + " \"\"\"Get exchange rate for a currency pair.\"\"\"\n", + " from_symbol, to_symbol = currency_pair.split(\"/\")\n", + " exchange_data = openbb.forex.models.av.get_quote(\n", + " to_symbol=to_symbol, from_symbol=from_symbol\n", + " )\n", + " return exchange_data[\"Realtime Currency Exchange Rate\"][\"5. Exchange Rate\"][:6]\n", + "\n", + "\n", + "def get_candle_widget(\n", + " output: Optional[ipw.Output],\n", + " to_symbol: str,\n", + " from_symbol: str,\n", + ") -> ipw.Output:\n", + " \"\"\"Plot a candle chart for a currency pair.\"\"\"\n", + " start_date = datetime.now() - timedelta(days=1)\n", + " start_date = start_date.strftime(\"%Y-%m-%d\")\n", + "\n", + " exchange_rate_data = openbb.forex.models.av.get_historical(\n", + " to_symbol=to_symbol,\n", + " from_symbol=from_symbol,\n", + " start_date=start_date,\n", + " resolution=\"i\",\n", + " interval=\"15\",\n", + " )\n", + " fig, ax = plt.subplots(1, 1, figsize=(11, 4))\n", + " openbb.forex.candle(\n", + " data=exchange_rate_data,\n", + " to_symbol=to_symbol,\n", + " from_symbol=from_symbol,\n", + " external_axes=[ax],\n", + " )\n", + "\n", + " ax.set_xlim(0, len(exchange_rate_data.index))\n", + " fig.canvas.header_visible = False\n", + " fig.canvas.footer_visible = False\n", + " with output:\n", + " output.clear_output(True)\n", + " fig.canvas.show()\n", + " return output\n", + "\n", + "\n", + "def on_dropdown_change(change):\n", + " \"\"\"Update charts on change of dropdown selection.\"\"\"\n", + " if change[\"type\"] == \"change\" and change[\"name\"] == \"value\":\n", + " output = ipw.Output()\n", + " get_candle_widget(\n", + " to_symbol=to_widget.value,\n", + " from_symbol=from_widget.value,\n", + " )\n", + " dashboard.children[\"Candle\"] = output" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c768d15-37ad-441b-b069-506c4bdbb9a7", + "metadata": {}, + "outputs": [], + "source": [ + "exchange_rates = {\n", + " \"EUR/USD\": {\"latest\": None, \"previous\": None},\n", + " \"USD/JPY\": {\"latest\": None, \"previous\": None},\n", + " \"GBP/USD\": {\"latest\": None, \"previous\": None},\n", + " \"AUD/USD\": {\"latest\": None, \"previous\": None},\n", + " \"USD/CAD\": {\"latest\": None, \"previous\": None},\n", + " # \"USD/CNY\": {\"latest\": None, \"previous\": None},\n", + " # \"USD/CHF\": {\"latest\": None, \"previous\": None},\n", + " # \"USD/HKD\": {\"latest\": None, \"previous\": None},\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4968ab89-aafe-4234-933c-963bd672715d", + "metadata": {}, + "outputs": [], + "source": [ + "def compose_widgets():\n", + " widgets = {}\n", + " for currency_pair in exchange_rates:\n", + " price = exchange_rates[currency_pair][\"latest\"]\n", + " price_color = \"neutral_color\"\n", + " if exchange_rates[currency_pair][\"previous\"] is not None:\n", + " if (\n", + " exchange_rates[currency_pair][\"latest\"]\n", + " > exchange_rates[currency_pair][\"previous\"]\n", + " ):\n", + " price_color = \"up_color\"\n", + " elif (\n", + " exchange_rates[currency_pair][\"latest\"]\n", + " < exchange_rates[currency_pair][\"previous\"]\n", + " ):\n", + " price_color = \"down_color\"\n", + " widgets[currency_pair] = ipw.HTML(\n", + " price_card(ticker=currency_pair, price=price, price_color=price_color)\n", + " )\n", + " return widgets" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f35d1500-a411-4cff-94ee-d65d4d1810f9", + "metadata": {}, + "outputs": [], + "source": [ + "# for currency_code in rates_to_usd:\n", + "# rates_to_usd[currency_code][\"previous\"] = rates_to_usd[currency_code][\"latest\"]\n", + "# rates_to_usd[currency_code][\"latest\"] = get_rate_against_usd(currency_code)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8a72837b-4ddd-4c01-a493-ebb430202cbc", + "metadata": {}, + "outputs": [], + "source": [ + "widgets = compose_widgets()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c636543c-42fe-4dd6-a900-1552a3c28c7e", + "metadata": {}, + "outputs": [], + "source": [ + "currency_list = openbb.forex.models.av.get_currency_list()\n", + "from_widget = ipw.Dropdown(\n", + " options=currency_list,\n", + " value=\"EUR\",\n", + " description=\"From:\",\n", + " disabled=False,\n", + " layout=ipw.Layout(margin=\"130\"),\n", + ")\n", + "to_widget = ipw.Dropdown(\n", + " options=currency_list,\n", + " value=\"USD\",\n", + " description=\"To:\",\n", + " disabled=False,\n", + ")\n", + "exchange_selection = ipw.VBox([from_widget, to_widget])\n", + "\n", + "widgets[\"Select\"] = exchange_selection" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9d069845-d622-4878-8b18-b385c6cb7a7f", + "metadata": {}, + "outputs": [], + "source": [ + "output = ipw.Output()\n", + "output = get_candle_widget(\n", + " output=output,\n", + " to_symbol=to_widget.value,\n", + " from_symbol=from_widget.value,\n", + ")\n", + "widgets[\"Candle\"] = output" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "81c7e5c7-2e52-45ba-bf7d-aa240f95be2d", + "metadata": {}, + "outputs": [], + "source": [ + "from_widget.observe(on_dropdown_change)\n", + "to_widget.observe(on_dropdown_change)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a0c7d921-e5dc-4ce8-97f1-a59b414a676a", + "metadata": {}, + "outputs": [], + "source": [ + "dashboard = FlexLayout(\n", + " layout_config={\"borderLeft\": False, \"borderRight\": False, \"enableSection\": False},\n", + " style={\n", + " \"height\": \"calc(100vh - 80px)\",\n", + " \"backgroundColor\": \"rgb(0 0 0)\",\n", + " \"fontFamily\": \"Consolas\",\n", + " \"fontWeight\": 800,\n", + " },\n", + " header={\n", + " \"title\": \"Currencies\",\n", + " \"style\": {\n", + " \"backgroundColor\": \"rgb(0 0 0)\",\n", + " \"fontWeight\": 400,\n", + " \"fontSize\": \"28px\",\n", + " },\n", + " \"buttons\": [],\n", + " },\n", + " widgets=widgets,\n", + " template=os.path.join(\"widgets\", \"currencies.json\"),\n", + " editable=False,\n", + ")\n", + "dashboard" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5fedac44-5e09-44f0-bbbe-fbad7c8716cd", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3c850ba2-408b-4061-947e-5a1b8a284d22", + "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" + }, + "voila": { + "theme": "dark" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/openbb_terminal/dashboards/voila/forecast.ipynb b/openbb_terminal/dashboards/voila/forecast.ipynb new file mode 100644 index 00000000000..76cefb10c70 --- /dev/null +++ b/openbb_terminal/dashboards/voila/forecast.ipynb @@ -0,0 +1,320 @@ +{ + "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", + "warnings.filterwarnings(\"ignore\")\n", + "logging.basicConfig(level=logging.CRITICAL)\n", + "logging.getLogger(\"darts.models\").setLevel(logging.CRITICAL)\n", + "logging.getLogger(\"pytorch_lightning\").setLevel(logging.CRITICAL)\n", + "logging.getLogger(\"darts.models.forecasting.pl_forecasting_module\").setLevel(logging.CRITICAL)\n", + "logging.getLogger(\"darts.models.forecasting.tcn_model\").setLevel(logging.CRITICAL)\n", + "logging.getLogger(\"darts.models.forecasting.nbeats\").setLevel(logging.CRITICAL)\n", + "logging.getLogger(\"darts.models.forecasting.NHiTSModel\").setLevel(logging.CRITICAL)\n", + "logging.getLogger(\"darts.models.forecasting.nhits\").setLevel(logging.CRITICAL)\n", + "logging.getLogger(\"darts.models.forecasting.torch_forecasting_model\").setLevel(logging.CRITICAL)\n", + "logging.getLogger(\"darts.models.forecasting.forecasting_model\").setLevel(logging.CRITICAL)\n", + "logging.getLogger(\"darts.models.forecasting.baselines\").setLevel(logging.CRITICAL)\n", + "logging.getLogger(\"pytorch_lightning.accelerators.gpu\").setLevel(logging.CRITICAL)\n", + "logging.getLogger(\"darts.timeseries\").setLevel(logging.CRITICAL)\n", + "logging.getLogger(\"darts.utils.utils\").setLevel(logging.CRITICAL)\n", + "logging.getLogger(\"darts.utils.torch\").setLevel(logging.CRITICAL)\n", + "logging.getLogger(\"darts.models.forecasting.tft_model\").setLevel(logging.CRITICAL)\n", + "logging.getLogger(\"darts.models.forecasting.tft_submodels\").setLevel(logging.CRITICAL)\n", + "logging.getLogger(\"darts.utils.data.training_dataset\").setLevel(logging.CRITICAL)\n", + "logging.getLogger(\"darts.utils.data.horizon_based_dataset\").setLevel(logging.CRITICAL)\n", + "logging.getLogger(\"darts.utils.statistics\").setLevel(logging.CRITICAL)\n", + "logging.getLogger(\"darts.dataprocessing.transformers.scaler\").setLevel(logging.CRITICAL)\n", + "logging.getLogger(\"darts.models.forecasting.RNNModel\").setLevel(logging.CRITICAL)\n", + "logging.getLogger(\"darts.models.forecasting.BlockRNNModel\").setLevel(logging.CRITICAL)\n", + "logging.getLogger(\"darts.models.forecasting.NBEATSModel\").setLevel(logging.CRITICAL)\n", + "logging.getLogger(\"darts.dataprocessing.transformers.fittable_data_transformer\").setLevel(logging.CRITICAL)\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import matplotlib_inline.backend_inline\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 api\n", + "\n", + "%matplotlib inline\n", + "matplotlib_inline.backend_inline.set_matplotlib_formats(\"svg\")\n", + "api.theme.applyMPLstyle()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "beb36881", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " has_forecast = True\n", + " model_opts = {\n", + " \"expo\": api.forecast.expo,\n", + " \"theta\": api.forecast.theta,\n", + " \"linregr\": api.forecast.linregr,\n", + " \"regr\": api.forecast.regr,\n", + " \"rnn\": api.forecast.rnn,\n", + " \"brnn\": api.forecast.brnn,\n", + " \"nbeats\": api.forecast.nbeats,\n", + " \"tcn\": api.forecast.tcn,\n", + " \"trans\": api.forecast.trans,\n", + " \"tft\": api.forecast.tft,\n", + " }\n", + "\n", + " feat_engs = {\n", + " \"ema\": api.forecast.ema,\n", + " \"sto\": api.forecast.sto,\n", + " \"rsi\": api.forecast.rsi,\n", + " \"roc\": api.forecast.roc,\n", + " \"mom\": api.forecast.mom,\n", + " \"atr\": api.forecast.atr,\n", + " \"delta\": api.forecast.delta,\n", + " \"signal\": api.forecast.signal,\n", + " }\n", + "except AttributeError:\n", + " has_forecast = False\n", + " model_opts = {}\n", + " feat_engs = {}\n", + "\n", + "interval_opts = [\"1m\",\"2m\",\"5m\",\"15m\",\"30m\",\"60m\",\"90m\",\"1h\",\"1d\",\"5d\",\"1wk\",\"1mo\",\"3mo\"]\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", + "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.infos = {}\n", + "\n", + " def handle_changes(\n", + " self, past_covariates, start, end, interval, tickers, target_column, model, naive, forecast_only\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", + " self.df = format_df(self.df)\n", + " self.last_tickers = tickers\n", + " self.last_interval = interval\n", + " forecast_model = model_opts[model]\n", + " contains_covariates = has_parameter(forecast_model, \"past_covariates\")\n", + " \n", + " #Update Inputs\n", + " if list(target_widget.options) != [x for x in self.df.columns if x != \"date\"]:\n", + " target_widget.options = [x for x in self.df.columns if x != \"date\"]\n", + " return\n", + " if list(past_covs_widget.options) != [x for x in self.df.columns if x != \"date\"]:\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", + "\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", + " self.forecast_model = forecast_model\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", + " self.forecast_model(self.result, target_column=self.target_column, n_predict=5, **self.kwargs)\n", + " \n", + " def handle_eng(self, target, feature):\n", + " self.feature_target = target\n", + " self.feature_model = feat_engs[feature]\n", + "\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(options=list(model_opts), value=model_value, layout=widgets.Layout(width=\"8%\",))\n", + "past_covs_widget = widgets.SelectMultiple(options=[\"\"], value=[\"\"], layout=widgets.Layout(width=\"8%\",))\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(options=interval_opts, value=\"1d\", layout=w_auto, description=\"Interval\")\n", + "tickers_widget = widgets.Textarea(value=\"TSLA,\", layout=widgets.Layout(width=\"auto\", height=\"100%\"))\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(options=list(feat_engs.keys()), value=feat_value, description=\"Feature\")\n", + "add_widget = widgets.ToggleButton(value=False, description=\"Add Feature\", layout=widgets.Layout(align=\"flex_end\")) # 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(chart.handle_eng, {\"target\": column_widget,\"feature\": feat_widget})\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 = \"Use a comma after EVERY stock typed.
'\n", + "\n", + " app_contents = [widgets.HTML(title_html), controls, widgets.HTML(warning_html), stocks_view]\n", + " app = widgets.VBox(app_contents)\n", + "else:\n", + " title_html = \"The forecasting dependencies are not installed.
'\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.6" + }, + "metadata": { + "interpreter": { + "hash": "e896a00ead8b521528d79ac9ef24990696f2b751eb283ab8e0d078c9c4971ffc" + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/openbb_terminal/dashboards/voila/futures.ipynb b/openbb_terminal/dashboards/voila/futures.ipynb new file mode 100644 index 00000000000..375ca9da511 --- /dev/null +++ b/openbb_terminal/dashboards/voila/futures.ipynb @@ -0,0 +1,321 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "e9ad60bc", + "metadata": {}, + "outputs": [], + "source": [ + "import os, sys\n", + "import calendar\n", + "from datetime import datetime, timedelta\n", + "import pandas as pd\n", + "import ipywidgets as widgets\n", + "import plotly.graph_objs as go\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.core.config.paths import REPOSITORY_DIRECTORY" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "01e1841a-ab16-4111-9b45-8ac66b85ab9f", + "metadata": {}, + "outputs": [], + "source": [ + "full_path = REPOSITORY_DIRECTORY / \"custom_imports\" / \"dashboards\" / \"futures.csv\"\n", + "df = pd.read_csv(full_path)\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": [ + "import os, sys\n", + "\n", + "\n", + "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, 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\"),\n", + " title={\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, fig):\n", + " if visual == \"line\":\n", + " plot = go.Scatter(x=x, y=y, mode=\"lines\", name=name) # , connectgaps=True\n", + " if visual == \"scatter\":\n", + " plot = go.Scatter(x=x, y=y, mode=\"markers\", name=name)\n", + " if visual == \"candle\":\n", + " plot = go.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", + " fig.add_trace(plot)\n", + "\n", + "\n", + "def show_fig(fig):\n", + " config = {\"showTips\": False, \"scrollZoom\": True}\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]}=F\"].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", + " dfs = yf.download(raw_ticker, progress=False) # ,period=\"max\"\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 = go.Figure()\n", + " i = 0\n", + " if chart_type[0][:4] == \"Hist\":\n", + " for key, item in 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", + " i += 1\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 = \"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": "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" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/openbb_terminal/dashboards/voila/stocks.ipynb b/openbb_terminal/dashboards/voila/stocks.ipynb new file mode 100644 index 00000000000..a31938c85f6 --- /dev/null +++ b/openbb_terminal/dashboards/voila/stocks.ipynb @@ -0,0 +1,404 @@ +{ + "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 plotly.graph_objs as go\n", + "import yfinance as yf\n", + "import pandas as pd\n", + "from IPython.display import display" + ] + }, + { + "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", + " \"marketCap\",\n", + " \"beta\",\n", + " \"fiftyTwoWeekHigh\",\n", + " \"fiftyTwoWeekLow\",\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", + " \"marketCap\": \"M Cap\",\n", + " \"beta\": \"Beta\",\n", + " \"fiftyTwoWeekHigh\": \"52W High\",\n", + " \"fiftyTwoWeekLow\": \"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", + " \"marketCap\": 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", + " \"fiftyTwoWeekHigh\": lambda x: \"N/A\" if x is None else f\"${round(x,2)}\",\n", + " \"fiftyTwoWeekLow\": 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, 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", + " margin=dict(l=0, r=10, t=10, b=10),\n", + " autosize=False,\n", + " width=900,\n", + " height=height,\n", + " legend=dict(orientation=\"h\"),\n", + " title={\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):\n", + " if visual == \"line\":\n", + " plot = go.Scatter(x=x, y=y[data], mode=\"lines\", name=name, connectgaps=True)\n", + " if visual == \"scatter\":\n", + " plot = go.Scatter(x=x, y=y[data], mode=\"markers\", name=name)\n", + " if visual == \"candle\":\n", + " plot = go.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", + " fig.add_trace(plot)\n", + "\n", + "\n", + "def show_fig(fig):\n", + " config = {\"showTips\": False, \"scrollZoom\": True}\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 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] = [clean_data[x](infos[ticker].get(x, None)) for x in rows]\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", + " 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 = go.Figure()\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 = go.Figure()\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", + " if tickers and tickers[-1] == \",\":\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", + " self.infos[ticker] = yf.Ticker(ticker).info\n", + " for ticker in self.infos:\n", + " if ticker not in tickers:\n", + " self.infos.pop(ticker)\n", + " result = table_data(self.infos)\n", + " fig = go.Figure(\n", + " data=[\n", + " go.Table(\n", + " header=dict(\n", + " values=result.columns,\n", + " fill_color=\"lightgray\",\n", + " font=dict(color=\"black\"),\n", + " align=\"left\",\n", + " ),\n", + " cells=dict(\n", + " values=[result[x] for x in result.columns],\n", + " font=dict(color=\"black\"),\n", + " align=\"left\",\n", + " ),\n", + " )\n", + " ],\n", + " )\n", + " fig.update_layout(margin=dict(l=0, r=20, t=0, b=0), width=350)\n", + " show_fig(fig)" + ] + }, + { + "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 = \"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": "8f33793c-adc9-4bc1-8fd4-aeff5a00ccba", + "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" + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/openbb_terminal/dashboards/voila/vsurf.ipynb b/openbb_terminal/dashboards/voila/vsurf.ipynb new file mode 100644 index 00000000000..816c8b52128 --- /dev/null +++ b/openbb_terminal/dashboards/voila/vsurf.ipynb @@ -0,0 +1,162 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "68b664c7-c52d-42c1-b170-575370323d22", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import warnings\n", + "import sys\n", + "import ipywidgets as widgets\n", + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "import pandas as pd\n", + "from IPython.display import display\n", + "\n", + "warnings.filterwarnings(\"ignore\")\n", + "module_path = os.path.abspath(os.path.join(\"../../..\"))\n", + "\n", + "if module_path not in sys.path:\n", + " sys.path.append(module_path)\n", + "\n", + "from openbb_terminal import api as openbb" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6c98dc75-6325-43da-93a2-1b8ec96bd368", + "metadata": {}, + "outputs": [], + "source": [ + "z_dict = {\n", + " \"IV\": \"Implied Volatility\",\n", + " \"OI\": \"Open Interest\",\n", + " \"LP\": \"Last Price\",\n", + "}\n", + "strings = {\n", + " \"IV\": \"DTE: %{x}