mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-27 12:40:35 +00:00
Refactor reports menu and make it work on installer (#2878)
* start papermill rollback * move reports to templates folder * comment some stuff * refactor reports controller * change var names * clean ctrl class vars * small refactoring on metadata search * relocate some func * breaking changes * revert some changes * destroying changes * change menu appearance * change menu appeaerance again * update choices * change some assets locations * remove unused vars * change some docstrings * rename vars * its working * forgot one dash * omg its working * clean some code and doc * change func name * fix a bug and add some comments * fix economy report startup * add custom reports * allow custom reports * forgot to delete this * unused import * make somestuff more explicit, 5 min rule * add helper func * supress errors--no-verify * add some type checks * fix check ipynb func * check inputs in run * rewrite some integration tests * remove unused import * add some docs to website * add auto completion * warn if no forecast * add to do * add arg name if 1 arg * add papermill pointing to github main * add notebooks * fix terminal spec * relocated choices files * merge main * relocate website docs for reports * limit user choices * fix portfolio auto completion * limit choices fix * clean som edocstrings * limit user choices in run command * improve run parser * fix msg * add reports controller tests * relax crypto choices limit * contain commands * pass pre commit * flake8 * flake8 * ignore mypy * pylint * website * move docs to jupyter tab * add installer dep * remove etf * remove reports * update reports to main * handle events erros * rever reports to previous commits before main merge * add papermill installer strategies * add flags to terminal * avoid sdk crash * execute notebook in thread * remove unused sys * execute report in a child process * fix some notebook stuff * fix portfolio report * fix reports controller volatility * change api to sdk * move images * remove som unucessary installer imports * remove debugger warning * move ipykernel launcher * supress notebook crash * remove portfolio decorator * fix run command * forgot to change this * add images * fix portfolio report * fix crypto report * linting and tests * add run integration test * pylint * hide reports module args * try at supressing args * try at supreccing warning * solve some os issues * handle pm errors * fix warning unrecognized arguments * change comment * avoid crash when parameters not found * fix etf commands * minor cleanup * update forecasting check * fix some linting * fix futures test * pylint * limit user choices to prompt * limit user choices to prompt * supress warnings * revert poetry lock back to main * papermill pointing to main * flake8 * forgot requirements * Update helper_funcs.py * Update reports_model.py * choices Co-authored-by: hjoaquim <h.joaquim@campus.fct.unl.pt> Co-authored-by: James Simmons <53658028+simmonsj330@users.noreply.github.com>
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -28,7 +28,7 @@ openbb_terminal/portfolio/portfolios/*
|
||||
*.swp
|
||||
*.http
|
||||
.coverage.*
|
||||
!openbb_terminal/custom_reports/*/.ipynb
|
||||
!openbb_terminal/reports/templates/*.ipynb
|
||||
|
||||
# pyinstaller artifacts
|
||||
*.pyo
|
||||
|
||||
8
build/pyinstaller/hooks/hook-debugpy.py
Normal file
8
build/pyinstaller/hooks/hook-debugpy.py
Normal file
@@ -0,0 +1,8 @@
|
||||
import os
|
||||
|
||||
# This environment variable suppresses the warning:
|
||||
# 0.00s - Debugger warning: The os.path.realpath.__code__.co_filename (posixpath.py)
|
||||
# 0.00s - is not absolute, which may make the debugger miss breakpoints.
|
||||
# 0.00s - Note: Debugging will proceed. Set PYDEVD_DISABLE_FILE_VALIDATION=1 to disable this validation.
|
||||
|
||||
os.environ["PYDEVD_DISABLE_FILE_VALIDATION"] = "1"
|
||||
4
build/pyinstaller/terminal.spec
vendored
4
build/pyinstaller/terminal.spec
vendored
@@ -62,6 +62,7 @@ added_files = [
|
||||
"pymongo",
|
||||
),
|
||||
(os.path.join(pathex, "bson"), "bson"),
|
||||
(os.path.join(pathex, "debugpy", "_vendored"), os.path.join("debugpy", "_vendored")),
|
||||
(".env", "."),
|
||||
]
|
||||
|
||||
@@ -89,6 +90,7 @@ hidden_imports = [
|
||||
"bson",
|
||||
"_sysconfigdata__darwin_darwin",
|
||||
"prophet",
|
||||
"debugpy",
|
||||
]
|
||||
|
||||
|
||||
@@ -100,7 +102,7 @@ analysis_kwargs = dict(
|
||||
hiddenimports=hidden_imports,
|
||||
hookspath=["build/pyinstaller/hooks"],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
runtime_hooks=["build/pyinstaller/hooks/hook-debugpy.py"],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
|
||||
@@ -32,3 +32,5 @@ USER_PORTFOLIO_DATA_DIRECTORY = USER_DATA_DIRECTORY / "portfolio"
|
||||
USER_ROUTINES_DIRECTORY = USER_DATA_DIRECTORY / "routines"
|
||||
USER_DATA_SOURCES_DEFAULT_FILE = USER_DATA_DIRECTORY / "data_sources_default.json"
|
||||
USER_PRESETS_DIRECTORY = USER_DATA_DIRECTORY / "presets"
|
||||
USER_REPORTS_DIRECTORY = USER_DATA_DIRECTORY / "reports"
|
||||
USER_CUSTOM_REPORTS_DIRECTORY = USER_DATA_DIRECTORY / "reports" / "custom reports"
|
||||
|
||||
@@ -43,12 +43,13 @@ dirs_list = [
|
||||
SETTINGS_DIRECTORY,
|
||||
USER_DATA_DIRECTORY,
|
||||
USER_DATA_DIRECTORY / "styles",
|
||||
USER_DATA_DIRECTORY / "reports",
|
||||
USER_DATA_DIRECTORY / "reports" / "custom reports",
|
||||
USER_CUSTOM_IMPORTS_DIRECTORY,
|
||||
USER_CUSTOM_IMPORTS_DIRECTORY / "econometrics",
|
||||
USER_CUSTOM_IMPORTS_DIRECTORY / "stocks",
|
||||
USER_CUSTOM_IMPORTS_DIRECTORY / "dashboards",
|
||||
USER_EXPORTS_DIRECTORY,
|
||||
USER_EXPORTS_DIRECTORY / "reports",
|
||||
USER_PORTFOLIO_DATA_DIRECTORY,
|
||||
USER_PORTFOLIO_DATA_DIRECTORY / "views",
|
||||
USER_PORTFOLIO_DATA_DIRECTORY / "holdings",
|
||||
|
||||
@@ -153,7 +153,7 @@ def parse_and_split_input(an_input: str, custom_filters: List) -> List[str]:
|
||||
# everything from ` -f ` to the next known extension
|
||||
file_flag = r"(\ -f |\ --file )"
|
||||
up_to = r".*?"
|
||||
known_extensions = r"(\.xlsx|.csv|.xls|.tsv|.json|.yaml|.ini|.openbb)"
|
||||
known_extensions = r"(\.xlsx|.csv|.xls|.tsv|.json|.yaml|.ini|.openbb|.ipynb)"
|
||||
unix_path_arg_exp = f"({file_flag}{up_to}{known_extensions})"
|
||||
|
||||
# Add custom expressions to handle edge cases of individual controllers
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import sys
|
||||
from typing import Optional
|
||||
import logging
|
||||
import os
|
||||
@@ -19,6 +20,23 @@ def is_jupyter() -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def is_papermill() -> bool:
|
||||
"""Check if session is being launched with args '-m ipykernel_launcher',
|
||||
thus coming from papermill Popen. See 'ipykernel_launcher' in reports_model
|
||||
for more detail.
|
||||
"""
|
||||
|
||||
return all(
|
||||
i in sys.argv
|
||||
for i in [
|
||||
"-m",
|
||||
"ipykernel_launcher",
|
||||
"-f",
|
||||
"--HistoryManager.hist_file=:memory:",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def inputhook(inputhook_context):
|
||||
while not inputhook_context.input_is_ready():
|
||||
try:
|
||||
@@ -32,7 +50,7 @@ def inputhook(inputhook_context):
|
||||
history_file = os.path.join(os.path.expanduser("~"), ".openbb_terminal.his")
|
||||
|
||||
try:
|
||||
if not is_jupyter():
|
||||
if not is_jupyter() and not is_papermill():
|
||||
session: Optional[PromptSession] = PromptSession(
|
||||
history=FileHistory(history_file)
|
||||
)
|
||||
|
||||
13
openbb_terminal/miscellaneous/i18n/en.yml
vendored
13
openbb_terminal/miscellaneous/i18n/en.yml
vendored
@@ -80,15 +80,14 @@ en:
|
||||
forex: foreign exchanges, quotes, forward rates for currency pairs and oanda integration
|
||||
funds: mutual funds search, overview, holdings and sector weights
|
||||
futures: commodities, bonds, index, bitcoin and forex
|
||||
futures/search: search for a future based on category or exchange
|
||||
futures/historical: get historical timeseries
|
||||
futures/curve: get futures curves
|
||||
alternative: alternative datasets, such as COVID and open source metrics
|
||||
econometrics: statistical and quantitative methods for relationships between datasets
|
||||
forecast: timeseries forecasting with machine learning
|
||||
portfolio: perform portfolio optimization and look at portfolio performance and attribution
|
||||
dashboards: interactive dashboards using voila and jupyter notebooks
|
||||
reports: customizable research reports through jupyter notebooks
|
||||
futures/search: search for a future based on category or exchange
|
||||
futures/historical: get historical timeseries
|
||||
futures/curve: get futures curves
|
||||
stocks/search: search a specific stock ticker for analysis
|
||||
stocks/load: load a specific stock ticker and additional info for analysis
|
||||
stocks/_ticker: Stock
|
||||
@@ -1018,7 +1017,11 @@ en:
|
||||
dashboards/shortdata: finra shortdata analysis
|
||||
dashboards/crypto: cryptocurrency exchange rates against USD
|
||||
dashboards/futures: historical futures performance
|
||||
reports/_reports_: Select one of the following report
|
||||
reports: customizable research reports through jupyter notebooks
|
||||
reports/_reports_: Build and run your custom reports or try one of our templates
|
||||
reports/_OpenBB_reports_: OpenBB reports
|
||||
reports/_Custom_reports_: Custom reports
|
||||
reports/run: Run a Jupyter notebook from OpenBBUserData/reports/custom reports
|
||||
forecast/_disclaimer_: DISCLAIMER
|
||||
forecast/_data_loc: Looking for data in
|
||||
forecast/load: load a dataset from csv
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
reports
|
||||
|
||||
## defaults
|
||||
crypto
|
||||
|
||||
## simple coin
|
||||
crypto BTC
|
||||
crypto --symbol BTC
|
||||
|
||||
## small-cap coin
|
||||
crypto STEPN
|
||||
crypto --symbol STEPN
|
||||
|
||||
## shitcoins
|
||||
crypto LUCKY
|
||||
crypto --symbol LUCKY
|
||||
|
||||
exit
|
||||
|
||||
@@ -2,7 +2,8 @@ reports
|
||||
|
||||
economy
|
||||
|
||||
# Test with random tickers
|
||||
# Test with random tickers, that should be ignored by the report.
|
||||
economy AAPL
|
||||
economy --symbol MSFT
|
||||
|
||||
exit
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
reports
|
||||
|
||||
## defaults
|
||||
equity
|
||||
|
||||
## simple ticker
|
||||
equity AAPL
|
||||
equity --symbol AAPL
|
||||
|
||||
## Non-US ticker
|
||||
equity NXPI
|
||||
equity --symbol NXPI
|
||||
|
||||
## Non-US ticker with suffix
|
||||
equity PETR3.SA
|
||||
equity --symbol PETR3.SA
|
||||
|
||||
exit
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
reports
|
||||
|
||||
etf VEA
|
||||
## defaults
|
||||
etf
|
||||
|
||||
etf VWCE
|
||||
etf --symbol VEA
|
||||
|
||||
etf --symbol VWCE
|
||||
|
||||
# test with random pairs
|
||||
etf ABCEF
|
||||
etf --symbol ABCEF
|
||||
|
||||
exit
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
reports
|
||||
|
||||
forex USDEUR
|
||||
## defaults
|
||||
forex
|
||||
|
||||
forex --symbol USDEUR
|
||||
|
||||
# Test with emerging pairs
|
||||
forex USDVND
|
||||
forex --symbol USDVND
|
||||
|
||||
# Test with invalid pairs
|
||||
forex USDABCXYZ
|
||||
forex --symbol USDABCXYZ
|
||||
|
||||
exit
|
||||
|
||||
10
openbb_terminal/miscellaneous/scripts/test_reports_run.openbb
vendored
Normal file
10
openbb_terminal/miscellaneous/scripts/test_reports_run.openbb
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
reports
|
||||
|
||||
## defaults
|
||||
run
|
||||
|
||||
## Invalid inputs
|
||||
run --file dummyfile.csv
|
||||
run --file dummyfile.ipynb
|
||||
|
||||
exit
|
||||
@@ -223,7 +223,7 @@ def get_info_from_ticker(ticker: str) -> list:
|
||||
|
||||
filename = "tickers_info.csv"
|
||||
|
||||
file_path = Path(str(DEFAULT_HOLDINGS_PATH), filename)
|
||||
file_path = Path(str(USER_PORTFOLIO_DATA_DIRECTORY), filename)
|
||||
|
||||
if file_path.is_file() and os.stat(file_path).st_size > 0:
|
||||
# file exists and is not empty, so append if necessary
|
||||
|
||||
@@ -56,7 +56,6 @@ class PortfolioModel:
|
||||
|
||||
"""
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def __init__(self, orderbook: pd.DataFrame = pd.DataFrame()):
|
||||
"""Initialize PortfolioModel class"""
|
||||
|
||||
@@ -129,7 +128,6 @@ class PortfolioModel:
|
||||
return df
|
||||
|
||||
@staticmethod
|
||||
@log_start_end(log=logger)
|
||||
def read_orderbook(path: str) -> pd.DataFrame:
|
||||
"""Static method to read orderbook from file
|
||||
|
||||
@@ -148,23 +146,24 @@ class PortfolioModel:
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def preprocess_orderbook(self):
|
||||
"""Method to preprocess, format and compute auxiliary fields"""
|
||||
"""Method to preprocess, format and compute auxiliary fields
|
||||
|
||||
# Preprocessing steps:
|
||||
# 0. If optional fields not in the orderbook add missing
|
||||
# 1. Convert Date to datetime
|
||||
# 2. Sort orderbook by date
|
||||
# 3. Capitalize Ticker and Type [of instrument...]
|
||||
# 4. Translate side: ["deposit", "buy"] -> 1 and ["withdrawal", "sell"] -> -1
|
||||
# 5. Convert quantity to signed integer
|
||||
# 6. Determining the investment/divestment value
|
||||
# 7. Reformat crypto tickers to yfinance format (e.g. BTC -> BTC-USD)
|
||||
# 8. Reformat STOCK/ETF tickers to yfinance format if ISIN provided
|
||||
# 9. Remove unsupported ISINs that came out empty
|
||||
# 10. Create tickers dictionary with structure {'Type': [Ticker]}
|
||||
# 11. Create list with tickers except cash
|
||||
# 12. Save orderbook inception date
|
||||
# 13. Populate fields Sector, Industry and Country
|
||||
Preprocessing steps:
|
||||
0. If optional fields not in the orderbook add missing
|
||||
1. Convert Date to datetime
|
||||
2. Sort orderbook by date
|
||||
3. Capitalize Ticker and Type [of instrument...]
|
||||
4. Translate side: ["deposit", "buy"] -> 1 and ["withdrawal", "sell"] -> -1
|
||||
5. Convert quantity to signed integer
|
||||
6. Determining the investment/divestment value
|
||||
7. Reformat crypto tickers to yfinance format (e.g. BTC -> BTC-USD)
|
||||
8. Reformat STOCK/ETF tickers to yfinance format if ISIN provided
|
||||
9. Remove unsupported ISINs that came out empty
|
||||
10. Create tickers dictionary with structure {'Type': [Ticker]}
|
||||
11. Create list with tickers except cash
|
||||
12. Save orderbook inception date
|
||||
13. Populate fields Sector, Industry and Country
|
||||
"""
|
||||
|
||||
try:
|
||||
console.print(" Preprocessing orderbook: ", end="")
|
||||
@@ -412,7 +411,6 @@ class PortfolioModel:
|
||||
full_shares: bool
|
||||
whether to mimic the portfolio trades exactly (partial shares) or round down the
|
||||
quantity to the nearest number.
|
||||
|
||||
"""
|
||||
|
||||
console.print("\n Loading benchmark: ", end="")
|
||||
|
||||
1245
openbb_terminal/reports/crypto.ipynb
vendored
1245
openbb_terminal/reports/crypto.ipynb
vendored
File diff suppressed because it is too large
Load Diff
100
openbb_terminal/reports/data/crypto_tickers.csv
vendored
Normal file
100
openbb_terminal/reports/data/crypto_tickers.csv
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
Symbol
|
||||
BTC
|
||||
ETH
|
||||
BNB
|
||||
ADA
|
||||
DOT
|
||||
XRP
|
||||
UNI
|
||||
LTC
|
||||
LINK
|
||||
THETA
|
||||
USDC
|
||||
BCH
|
||||
XLM
|
||||
LUNA
|
||||
WBTC
|
||||
DOGE
|
||||
VET
|
||||
CRO
|
||||
FIL
|
||||
AAVE
|
||||
TRX
|
||||
MIOTA
|
||||
ATOM
|
||||
SOL
|
||||
EOS
|
||||
AVAX
|
||||
XMR
|
||||
BSV
|
||||
KSM
|
||||
FTT
|
||||
XTZ
|
||||
XEM
|
||||
BTT
|
||||
ALGO
|
||||
NEO
|
||||
CHZ
|
||||
DAI
|
||||
HT
|
||||
HBAR
|
||||
EGLD
|
||||
SUSHI
|
||||
SNX
|
||||
DASH
|
||||
TFUEL
|
||||
LEO
|
||||
GRT
|
||||
DCR
|
||||
MKR
|
||||
ZIL
|
||||
COMP
|
||||
MATIC
|
||||
NEAR
|
||||
ENJ
|
||||
RVN
|
||||
BAT
|
||||
NPXS
|
||||
ZEC
|
||||
CAKE
|
||||
ONE
|
||||
MANA
|
||||
NEXO
|
||||
UMA
|
||||
ETC
|
||||
BNT
|
||||
CHSB
|
||||
RUNE
|
||||
YFI
|
||||
HOT
|
||||
RSR
|
||||
ICX
|
||||
CEL
|
||||
ZRX
|
||||
WAVES
|
||||
FTM
|
||||
REN
|
||||
SC
|
||||
REV
|
||||
VGX
|
||||
ONT
|
||||
DGB
|
||||
DENT
|
||||
OKB
|
||||
IOST
|
||||
OMG
|
||||
PAX
|
||||
CELO
|
||||
1INCH
|
||||
NANO
|
||||
NANO
|
||||
QTUM
|
||||
LRC
|
||||
OCEAN
|
||||
CRV
|
||||
KNC
|
||||
ZEN
|
||||
MVL
|
||||
BTG
|
||||
LSK
|
||||
XVG
|
||||
|
101
openbb_terminal/reports/data/etf_tickers.csv
vendored
Normal file
101
openbb_terminal/reports/data/etf_tickers.csv
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
Symbol
|
||||
SPY
|
||||
IVV
|
||||
VOO
|
||||
VTI
|
||||
QQQ
|
||||
VTV
|
||||
VEA
|
||||
BND
|
||||
AGG
|
||||
IEFA
|
||||
VUG
|
||||
VWO
|
||||
IJR
|
||||
VIG
|
||||
IJH
|
||||
IEMG
|
||||
IWF
|
||||
GLD
|
||||
IWM
|
||||
IWD
|
||||
VO
|
||||
BNDX
|
||||
VYM
|
||||
VXUS
|
||||
VCSH
|
||||
EFA
|
||||
VB
|
||||
BSV
|
||||
XLE
|
||||
ITOT
|
||||
VGT
|
||||
VCIT
|
||||
XLV
|
||||
SCHD
|
||||
XLK
|
||||
LQD
|
||||
VNQ
|
||||
MUB
|
||||
SHY
|
||||
VEU
|
||||
RSP
|
||||
SCHX
|
||||
USMV
|
||||
XLF
|
||||
IVW
|
||||
TIP
|
||||
BIL
|
||||
DIA
|
||||
IAU
|
||||
IWB
|
||||
IWR
|
||||
GOVT
|
||||
TLT
|
||||
IXUS
|
||||
SHV
|
||||
SCHF
|
||||
VV
|
||||
JPST
|
||||
IVE
|
||||
VBR
|
||||
IEF
|
||||
DGRO
|
||||
IGSB
|
||||
VT
|
||||
SDY
|
||||
EEM
|
||||
ESGU
|
||||
DVY
|
||||
VTEB
|
||||
MBB
|
||||
SCHB
|
||||
VTIP
|
||||
IUSB
|
||||
VGSH
|
||||
MDY
|
||||
QUAL
|
||||
VHT
|
||||
XLU
|
||||
XLP
|
||||
ACWI
|
||||
VOE
|
||||
SCHP
|
||||
DFAC
|
||||
VMBS
|
||||
XLY
|
||||
PFF
|
||||
EMB
|
||||
SPLG
|
||||
SCHG
|
||||
JEPI
|
||||
EFV
|
||||
SCHA
|
||||
VXF
|
||||
HYG
|
||||
BIV
|
||||
VGK
|
||||
STIP
|
||||
IWS
|
||||
SPYG
|
||||
VGIT
|
||||
|
52
openbb_terminal/reports/data/stocks_tickers.csv
vendored
Normal file
52
openbb_terminal/reports/data/stocks_tickers.csv
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
Symbol
|
||||
AAPL
|
||||
ABBV
|
||||
ABT
|
||||
ACN
|
||||
AMZN
|
||||
ASML
|
||||
AVGO
|
||||
AZN
|
||||
BABA
|
||||
BAC
|
||||
BMY
|
||||
BRK-A
|
||||
COP
|
||||
COST
|
||||
CRM
|
||||
CSCO
|
||||
CVX
|
||||
DHR
|
||||
DIS
|
||||
GOOGL
|
||||
HD
|
||||
JNJ
|
||||
JPM
|
||||
KO
|
||||
LLY
|
||||
MA
|
||||
MCD
|
||||
META
|
||||
MRK
|
||||
MSFT
|
||||
NEE
|
||||
NVDA
|
||||
NVO
|
||||
NVS
|
||||
ORCL
|
||||
PEP
|
||||
PFE
|
||||
PG
|
||||
TM
|
||||
TMO
|
||||
TMUS
|
||||
TSLA
|
||||
TSM
|
||||
UNH
|
||||
UPS
|
||||
V
|
||||
VZ
|
||||
WFC
|
||||
WMT
|
||||
XOM
|
||||
Summ
|
||||
|
@@ -1,28 +1,19 @@
|
||||
"""Reports Controller Module."""
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
|
||||
# pylint: disable=R1732, R0912
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import webbrowser
|
||||
from ast import literal_eval
|
||||
from datetime import datetime
|
||||
from typing import List
|
||||
import importlib
|
||||
import contextlib
|
||||
import io
|
||||
|
||||
from nbconvert.nbconvertapp import NbConvertApp
|
||||
from nbconvert.exporters import ScriptExporter
|
||||
from nbconvert.writers import FilesWriter
|
||||
from matplotlib import pyplot as plt
|
||||
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from openbb_terminal import feature_flags as obbff
|
||||
from openbb_terminal.core.config.paths import USER_EXPORTS_DIRECTORY
|
||||
from openbb_terminal.core.config.paths import (
|
||||
USER_CUSTOM_REPORTS_DIRECTORY,
|
||||
)
|
||||
from openbb_terminal.helper_funcs import parse_simple_args
|
||||
from openbb_terminal.reports import reports_model
|
||||
from openbb_terminal.custom_prompt_toolkit import NestedCompleter
|
||||
from openbb_terminal.decorators import log_start_end
|
||||
from openbb_terminal.menu import session
|
||||
@@ -35,283 +26,239 @@ logger = logging.getLogger(__name__)
|
||||
class ReportController(BaseController):
|
||||
"""Report Controller class."""
|
||||
|
||||
reports_folder = Path(__file__).parent
|
||||
|
||||
report_names = [
|
||||
notebooks[:-6]
|
||||
for notebooks in os.listdir(reports_folder)
|
||||
if notebooks.endswith(".ipynb")
|
||||
CHOICES_COMMANDS: List[str] = [
|
||||
"crypto",
|
||||
"economy",
|
||||
"equity",
|
||||
"etf",
|
||||
"forecast",
|
||||
"forex",
|
||||
"portfolio",
|
||||
"run",
|
||||
"load",
|
||||
]
|
||||
|
||||
ids_reports = [str(val + 1) for val in range(len(report_names))]
|
||||
|
||||
d_id_to_report_name = {}
|
||||
for id_report, report_name in enumerate(report_names):
|
||||
d_id_to_report_name[str(id_report + 1)] = report_name
|
||||
|
||||
d_params = {}
|
||||
|
||||
max_len_name = max(len(name) for name in report_names) + 2
|
||||
reports_opts = ""
|
||||
for k, report_to_run in d_id_to_report_name.items():
|
||||
# Crawl data to look into what
|
||||
notebook_file = Path(__file__).parent / report_to_run
|
||||
|
||||
# Open notebook with report template
|
||||
with open(str(notebook_file) + ".ipynb") as n_file:
|
||||
notebook_content = n_file.read()
|
||||
|
||||
# Look for the metadata cell to understand if there are parameters required by the report
|
||||
metadata_cell = """"metadata": {\n "tags": [\n "parameters"\n ]\n },\n "outputs":"""
|
||||
|
||||
# Locate position of the data of interest and get parameters
|
||||
notebook_metadata_content = notebook_content[
|
||||
notebook_content.find(metadata_cell) : # noqa: E203
|
||||
]
|
||||
cell_start = 'source": '
|
||||
cell_end = "]"
|
||||
params = notebook_metadata_content[
|
||||
notebook_metadata_content.find(
|
||||
cell_start
|
||||
) : notebook_metadata_content.find( # noqa: E203
|
||||
cell_end, notebook_metadata_content.find(cell_start)
|
||||
)
|
||||
+ 1
|
||||
]
|
||||
# Make sure that the parameters provided are relevant
|
||||
if "parameters" in notebook_content:
|
||||
l_params = [
|
||||
param.split("=")[0][:-1]
|
||||
for param in literal_eval(params.strip('source": '))
|
||||
if param[0] not in ["#", "\n"]
|
||||
]
|
||||
def_params = [
|
||||
param.split("=")[1][2:-1]
|
||||
for param in literal_eval(params.strip('source": '))
|
||||
if param[0] not in ["#", "\n"]
|
||||
]
|
||||
# to ensure default value is correctly selected
|
||||
for param in range(len(def_params) - 1):
|
||||
def_params[param] = def_params[param][:-1]
|
||||
|
||||
if "report_name" in l_params:
|
||||
l_params.remove("report_name")
|
||||
d_params[report_to_run] = [l_params, def_params]
|
||||
|
||||
# On the menu of choices add the parameters necessary for each template report
|
||||
if len(l_params) > 1 or not l_params:
|
||||
args = f"<{'> <'.join(l_params)}>"
|
||||
else:
|
||||
args = f"<{l_params[0]}>"
|
||||
|
||||
reports_opts += (
|
||||
f" {k}. {report_to_run}"
|
||||
+ f"{(max_len_name-len(report_to_run))*' '} "
|
||||
+ f"{args if args != '<>' else ''}\n"
|
||||
)
|
||||
CHOICES_MENUS = report_names + ids_reports + ["r", "reset"]
|
||||
PATH = "/reports/"
|
||||
|
||||
REPORTS: List[str] = reports_model.get_reports_available()
|
||||
REPORTS = sorted(list(set(CHOICES_COMMANDS).intersection(REPORTS)))
|
||||
PARAMETERS_DICT: Dict[str, Any] = {}
|
||||
|
||||
def __init__(self, queue: List[str] = None):
|
||||
"""Constructor"""
|
||||
|
||||
super().__init__(queue)
|
||||
self.update_choices()
|
||||
|
||||
def update_choices(self):
|
||||
"""Update controller choices with reports available under templates folder."""
|
||||
|
||||
for report_name in self.REPORTS:
|
||||
# Extract report parameters do display on menu
|
||||
self.PARAMETERS_DICT[report_name] = reports_model.extract_parameters(
|
||||
str(reports_model.REPORTS_FOLDER / report_name)
|
||||
)
|
||||
|
||||
if session and obbff.USE_PROMPT_TOOLKIT:
|
||||
choices: dict = {c: {} for c in self.controller_choices}
|
||||
|
||||
choices["support"] = self.SUPPORT_CHOICES
|
||||
choices["about"] = self.ABOUT_CHOICES
|
||||
self.choices: dict = {c: {} for c in self.controller_choices}
|
||||
self.choices["run"] = {
|
||||
"--file": {c: None for c in reports_model.USER_REPORTS},
|
||||
"-f": "--file",
|
||||
"--parameters": {},
|
||||
"-p": "--parameters",
|
||||
}
|
||||
for report_name in self.REPORTS:
|
||||
|
||||
self.completer = NestedCompleter.from_nested_dict(choices)
|
||||
# Completer with limited user choices to avoid unforeseen problems
|
||||
self.choices[report_name] = {}
|
||||
for arg in self.PARAMETERS_DICT[report_name]:
|
||||
if report_name in reports_model.REPORT_CHOICES:
|
||||
self.choices[report_name][
|
||||
"--" + arg
|
||||
] = reports_model.REPORT_CHOICES[report_name]["--" + arg]
|
||||
|
||||
self.choices["support"] = self.SUPPORT_CHOICES
|
||||
self.choices["about"] = self.ABOUT_CHOICES
|
||||
|
||||
self.completer = NestedCompleter.from_nested_dict(self.choices)
|
||||
|
||||
def print_help(self):
|
||||
"""Print help."""
|
||||
|
||||
self.REPORTS = reports_model.get_reports_available()
|
||||
self.REPORTS = sorted(
|
||||
list(set(self.CHOICES_COMMANDS).intersection(self.REPORTS))
|
||||
)
|
||||
self.update_choices()
|
||||
|
||||
mt = MenuText("reports/")
|
||||
mt.add_info("_reports_")
|
||||
mt.add_raw(f"[cmds]{self.reports_opts}[/cmds]")
|
||||
mt.add_raw("\n")
|
||||
mt.add_info("_OpenBB_reports_")
|
||||
MAX_LEN_NAME = max(len(name) for name in self.REPORTS) + 2
|
||||
templates_string = ""
|
||||
for report_name in self.REPORTS:
|
||||
|
||||
parameters_names = list(self.PARAMETERS_DICT[report_name].keys())
|
||||
|
||||
if len(parameters_names) > 1 or not parameters_names:
|
||||
args = f"<{'> <'.join(parameters_names)}>"
|
||||
else:
|
||||
args = f"<{parameters_names[0]}>"
|
||||
|
||||
templates_string += (
|
||||
f" [cmds]{report_name}[/cmds]"
|
||||
+ f"{(MAX_LEN_NAME-len(report_name))*' '} "
|
||||
+ f"[param]{args if args != '<>' else ''}[/param]\n"
|
||||
)
|
||||
mt.add_raw(f"{templates_string}")
|
||||
mt.add_raw("\n")
|
||||
mt.add_info("_Custom_reports_")
|
||||
mt.add_cmd("run")
|
||||
console.print(text=mt.menu_text, menu="Reports - WORK IN PROGRESS")
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def switch(self, an_input: str):
|
||||
"""Process and dispatch input.
|
||||
def call_etf(self, other_args: List[str]):
|
||||
self.run_report("etf", other_args)
|
||||
|
||||
Parameters
|
||||
----------
|
||||
an_input : str
|
||||
string with input arguments
|
||||
@log_start_end(log=logger)
|
||||
def call_forex(self, other_args: List[str]):
|
||||
self.run_report("forex", other_args)
|
||||
|
||||
Returns
|
||||
-------
|
||||
List[str]
|
||||
List of commands in the queue to execute
|
||||
"""
|
||||
# Empty command
|
||||
if not an_input:
|
||||
console.print("")
|
||||
return self.queue
|
||||
@log_start_end(log=logger)
|
||||
def call_portfolio(self, other_args: List[str]):
|
||||
self.run_report("portfolio", other_args)
|
||||
|
||||
# Navigation slash is being used
|
||||
if "/" in an_input:
|
||||
actions = an_input.split("/")
|
||||
@log_start_end(log=logger)
|
||||
def call_economy(self, other_args: List[str]):
|
||||
self.run_report("economy", other_args)
|
||||
|
||||
# Absolute path is specified
|
||||
if not actions[0]:
|
||||
an_input = "home"
|
||||
# Relative path so execute first instruction
|
||||
else:
|
||||
an_input = actions[0]
|
||||
@log_start_end(log=logger)
|
||||
def call_equity(self, other_args: List[str]):
|
||||
self.run_report("equity", other_args)
|
||||
|
||||
# Add all instructions to the queue
|
||||
for cmd in actions[1:][::-1]:
|
||||
if cmd:
|
||||
self.queue.insert(0, cmd)
|
||||
@log_start_end(log=logger)
|
||||
def call_crypto(self, other_args: List[str]):
|
||||
self.run_report("crypto", other_args)
|
||||
|
||||
(known_args, other_args) = self.parser.parse_known_args(an_input.split())
|
||||
|
||||
# Redirect commands to their correct functions
|
||||
if known_args.cmd:
|
||||
if known_args.cmd in ("..", "q"):
|
||||
known_args.cmd = "quit"
|
||||
elif known_args.cmd in ("?", "h"):
|
||||
known_args.cmd = "help"
|
||||
elif known_args.cmd == "r":
|
||||
known_args.cmd = "reset"
|
||||
|
||||
if known_args.cmd in ["quit", "help", "reset", "home", "exit", "cls"]:
|
||||
getattr(
|
||||
self,
|
||||
"call_" + known_args.cmd,
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
|
||||
return self.queue
|
||||
|
||||
# Execute the requested report
|
||||
if known_args.cmd in self.d_id_to_report_name:
|
||||
report_to_run = self.d_id_to_report_name[known_args.cmd]
|
||||
else:
|
||||
report_to_run = known_args.cmd
|
||||
|
||||
params = self.d_params[report_to_run][0]
|
||||
|
||||
# Check that the number of arguments match. We can't check validity of the
|
||||
# argument used because this depends on what the user will use it for in
|
||||
# the notebook. This is a downside of allowing the user to have this much
|
||||
# flexibility.
|
||||
if len(other_args) != len(params):
|
||||
console.print("Wrong number of arguments provided!")
|
||||
if len(params):
|
||||
console.print("Provide, in order:")
|
||||
for k, v in enumerate(params):
|
||||
console.print(f"{k+1}. {v}")
|
||||
else:
|
||||
console.print("No argument required.")
|
||||
console.print("")
|
||||
return []
|
||||
|
||||
args_to_output = f"_{'_'.join(other_args)}" if "_".join(other_args) else ""
|
||||
report_output_name = (
|
||||
f"{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
||||
+ "_"
|
||||
+ f"{report_to_run}{args_to_output}"
|
||||
)
|
||||
notebook_output = str(
|
||||
USER_EXPORTS_DIRECTORY / "reports" / report_output_name
|
||||
@log_start_end(log=logger)
|
||||
def call_forecast(self, other_args: List[str]):
|
||||
try:
|
||||
import darts # pyright: reportMissingImports=false # noqa: F401, E501 #pylint: disable=import-outside-toplevel, unused-import
|
||||
from darts import ( # pyright: reportMissingImports=false # noqa: F401, E501 #pylint: disable=import-outside-toplevel, unused-import
|
||||
utils,
|
||||
)
|
||||
|
||||
# only a single backslash appears in the .py file otherwise
|
||||
notebook_output = notebook_output.replace("\\", "\\\\")
|
||||
notebook_output = notebook_output.replace("\\", "\\\\")
|
||||
forecast = True
|
||||
except ImportError:
|
||||
forecast = False
|
||||
console.print(
|
||||
"\n'forecast' menu dependencies are not installed."
|
||||
" This part of the SDK will not be usable.\n\n"
|
||||
"For more information see the official documentation at: "
|
||||
"[blue]https://openbb-finance.github.io/OpenBBTerminal/sdk/[/blue]\n"
|
||||
)
|
||||
if forecast:
|
||||
self.run_report("forecast", other_args)
|
||||
|
||||
# gather params from user
|
||||
d_report_params = {}
|
||||
for idx, param in enumerate(params):
|
||||
d_report_params[param] = other_args[idx]
|
||||
@log_start_end(log=logger)
|
||||
def run_report(self, report_name: str, other_args: List[str]):
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog=report_name,
|
||||
description=f"Run {report_name} report.",
|
||||
)
|
||||
|
||||
d_report_params["report_name"] = notebook_output
|
||||
if report_name in self.PARAMETERS_DICT:
|
||||
|
||||
notebook_file = str(Path(__file__).parent / report_to_run)
|
||||
# Assign respective parameters as arguments
|
||||
for arg_name, arg_default in self.PARAMETERS_DICT[report_name].items():
|
||||
|
||||
with open(notebook_file + ".ipynb") as n_file:
|
||||
notebook_content = n_file.read()
|
||||
choices = reports_model.get_arg_choices(report_name, arg_name)
|
||||
|
||||
params.append("report_name") # re-add report_name
|
||||
|
||||
# replace params in notebook
|
||||
if "parameters" in notebook_content:
|
||||
params_to_change = self.d_params[report_to_run][1]
|
||||
for idx, param in enumerate(params_to_change):
|
||||
notebook_content = notebook_content.replace(
|
||||
param, d_report_params[params[idx]], 1
|
||||
)
|
||||
|
||||
notebook_file_copy = notebook_file + "copy"
|
||||
|
||||
with open(notebook_file_copy + ".ipynb", "w") as file:
|
||||
file.write(notebook_content)
|
||||
|
||||
# use nbconvert to switch to .py
|
||||
converter = NbConvertApp()
|
||||
converter.log_level = 40
|
||||
converter.export_format = "script"
|
||||
converter.exporter = ScriptExporter()
|
||||
converter.writer = FilesWriter()
|
||||
|
||||
converter.convert_single_notebook(notebook_file_copy + ".ipynb")
|
||||
|
||||
python_file = Path(notebook_file_copy + ".py")
|
||||
python_text = python_file.read_text().split("\n")
|
||||
|
||||
# remove lines with get_ipython
|
||||
lines = [
|
||||
line
|
||||
for line in python_text
|
||||
if len(line) == 0 or "get_ipython()" not in line
|
||||
]
|
||||
clean_code = "\n".join(lines)
|
||||
clean_code = re.sub(r"\n{2,}", "\n\n", clean_code)
|
||||
python_file.write_text(clean_code.strip())
|
||||
|
||||
# Check for existing matplotlib figures
|
||||
_figures_before = plt.get_fignums()
|
||||
|
||||
# Run the report
|
||||
try:
|
||||
notebook_execution_output = io.StringIO()
|
||||
with contextlib.redirect_stdout(notebook_execution_output):
|
||||
importlib.import_module(
|
||||
f"openbb_terminal.reports.{report_to_run}copy"
|
||||
)
|
||||
except Exception as error:
|
||||
console.print("[red]\nReport wasn't executed correctly.\n[/red]")
|
||||
console.print(f"[red]\nError : {error}\n[/red]")
|
||||
|
||||
# Close the figures that were created during report execution
|
||||
_figures_after = plt.get_fignums()
|
||||
for fig_number in _figures_after:
|
||||
if fig_number not in _figures_before:
|
||||
plt.close(fig_number)
|
||||
|
||||
notebook_output = notebook_output.replace("\\\\", "\\")
|
||||
notebook_output = notebook_output.replace("\\\\", "\\")
|
||||
report_output_path = notebook_output + ".html"
|
||||
|
||||
params.remove("report_name") # remove report_name
|
||||
|
||||
os.remove(notebook_file_copy + ".ipynb")
|
||||
os.remove(notebook_file_copy + ".py")
|
||||
|
||||
if Path(report_output_path).is_file():
|
||||
if obbff.OPEN_REPORT_AS_HTML:
|
||||
console.print(report_output_path)
|
||||
webbrowser.open(f"file://{report_output_path}")
|
||||
|
||||
console.print("")
|
||||
console.print(
|
||||
f"Exported: {report_output_path}",
|
||||
"\n",
|
||||
getattr(parser, "add_argument")(
|
||||
"--" + arg_name,
|
||||
type=str,
|
||||
default=arg_default,
|
||||
choices=choices,
|
||||
dest=arg_name,
|
||||
help=arg_name,
|
||||
)
|
||||
else:
|
||||
console.print("[red]\nReport couldn't be created.\n[/red]")
|
||||
|
||||
return self.queue
|
||||
if (
|
||||
other_args
|
||||
and "-" not in other_args[0][0]
|
||||
and len(self.PARAMETERS_DICT[report_name]) == 1
|
||||
):
|
||||
other_args.insert(
|
||||
0, "--" + list(self.PARAMETERS_DICT[report_name].keys())[0]
|
||||
)
|
||||
ns_parser = parse_simple_args(parser, other_args)
|
||||
|
||||
if ns_parser:
|
||||
parameters = vars(ns_parser)
|
||||
parameters.pop("help")
|
||||
reports_model.render_report(
|
||||
input_path=str(reports_model.REPORTS_FOLDER / report_name),
|
||||
args_dict=parameters,
|
||||
)
|
||||
|
||||
else:
|
||||
console.print("[red]Notebook not found![/red]\n")
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def call_run(self, other_args: List[str]):
|
||||
"""Process run command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="run",
|
||||
description=f"Run a notebook from this folder: '{str(USER_CUSTOM_REPORTS_DIRECTORY)}'.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-f",
|
||||
"--file",
|
||||
dest="file",
|
||||
choices=reports_model.USER_REPORTS,
|
||||
required="-h" not in other_args,
|
||||
help="The file to be loaded",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--parameters",
|
||||
nargs="+",
|
||||
dest="parameters",
|
||||
help="Report parameters with format 'name:value'.",
|
||||
)
|
||||
|
||||
ns_parser = self.parse_known_args_and_warn(parser, other_args)
|
||||
|
||||
if ns_parser:
|
||||
# Validate parameter inputs
|
||||
parameters_dict = {}
|
||||
if ns_parser.parameters:
|
||||
for p in ns_parser.parameters:
|
||||
if ":" in p:
|
||||
item = p.split(":")
|
||||
if item[1]:
|
||||
parameters_dict[item[0]] = item[1]
|
||||
else:
|
||||
console.print(
|
||||
f"[red]Bad format '{p}': empty value.[/red]\nExecuting with defaults.\n"
|
||||
)
|
||||
else:
|
||||
console.print(
|
||||
f"[red]Bad format '{p}': use format 'name:value'.[/red]\nExecuting with defaults.\n"
|
||||
)
|
||||
|
||||
if ns_parser.file:
|
||||
complete_file_path = str(USER_CUSTOM_REPORTS_DIRECTORY / ns_parser.file)
|
||||
if os.path.exists(complete_file_path):
|
||||
reports_model.render_report(
|
||||
input_path=complete_file_path, args_dict=parameters_dict
|
||||
)
|
||||
else:
|
||||
console.print(
|
||||
f"[red]Notebook '{ns_parser.file}' not found![/red]\n"
|
||||
)
|
||||
|
||||
420
openbb_terminal/reports/reports_model.py
Normal file
420
openbb_terminal/reports/reports_model.py
Normal file
@@ -0,0 +1,420 @@
|
||||
"""Reports Model Module."""
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import logging
|
||||
|
||||
# pylint: disable=R1732, R0912
|
||||
import os
|
||||
from pathlib import Path
|
||||
from threading import Thread
|
||||
import webbrowser
|
||||
from ast import literal_eval
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Union
|
||||
from ipykernel.kernelapp import IPKernelApp
|
||||
import papermill as pm
|
||||
import pandas as pd
|
||||
|
||||
from openbb_terminal import feature_flags as obbff
|
||||
from openbb_terminal.core.config.paths import (
|
||||
MISCELLANEOUS_DIRECTORY,
|
||||
USER_PORTFOLIO_DATA_DIRECTORY,
|
||||
USER_REPORTS_DIRECTORY,
|
||||
USER_CUSTOM_REPORTS_DIRECTORY,
|
||||
)
|
||||
from openbb_terminal.decorators import log_start_end
|
||||
from openbb_terminal.rich_config import console
|
||||
from openbb_terminal.forex.forex_controller import FX_TICKERS
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
CURRENT_LOCATION = Path(__file__)
|
||||
REPORTS_FOLDER = CURRENT_LOCATION.parent / "templates"
|
||||
|
||||
USER_REPORTS = {
|
||||
filepath.name: filepath
|
||||
for file_type in ["ipynb"]
|
||||
for filepath in USER_CUSTOM_REPORTS_DIRECTORY.rglob(f"*.{file_type}")
|
||||
}
|
||||
|
||||
# TODO: Trim available choices to avoid errors in notebooks.
|
||||
|
||||
etf_data_path = CURRENT_LOCATION.parent / "data" / "etf_tickers.csv"
|
||||
ETF_TICKERS = pd.read_csv(etf_data_path).iloc[:, 0].to_list()
|
||||
|
||||
crypto_data_path = CURRENT_LOCATION.parent / "data" / "crypto_tickers.csv"
|
||||
CRYPTO_TICKERS = pd.read_csv(crypto_data_path).iloc[:, 0].to_list()
|
||||
|
||||
stocks_data_path = CURRENT_LOCATION.parent / "data" / "stocks_tickers.csv"
|
||||
STOCKS_TICKERS = pd.read_csv(stocks_data_path).iloc[:, 0].to_list()
|
||||
|
||||
PORTFOLIO_HOLDINGS_FILES = {
|
||||
filepath.name: filepath
|
||||
for file_type in ["xlsx", "csv"]
|
||||
for filepath in (USER_PORTFOLIO_DATA_DIRECTORY / "holdings").rglob(f"*.{file_type}")
|
||||
}
|
||||
|
||||
PORTFOLIO_HOLDINGS_FILES.update(
|
||||
{
|
||||
filepath.name: filepath
|
||||
for file_type in ["xlsx", "csv"]
|
||||
for filepath in (
|
||||
MISCELLANEOUS_DIRECTORY / "portfolio_examples" / "holdings"
|
||||
).rglob(f"*.{file_type}")
|
||||
}
|
||||
)
|
||||
|
||||
REPORT_CHOICES = {
|
||||
"etf": {
|
||||
"--symbol": {c: None for c in ETF_TICKERS},
|
||||
},
|
||||
"forex": {
|
||||
"--symbol": {c: None for c in FX_TICKERS},
|
||||
},
|
||||
"portfolio": {
|
||||
"--orderbook": {c: None for c in PORTFOLIO_HOLDINGS_FILES},
|
||||
},
|
||||
"economy": None,
|
||||
"equity": {
|
||||
"--symbol": {c: None for c in STOCKS_TICKERS},
|
||||
},
|
||||
"crypto": {
|
||||
"--symbol": {c: None for c in CRYPTO_TICKERS},
|
||||
},
|
||||
"forecast": {
|
||||
"--symbol": {c: None for c in STOCKS_TICKERS + ETF_TICKERS},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def get_arg_choices(report_name: str, arg_name: str) -> Union[List[str], None]:
|
||||
"""Get argument choices from autocompletion for crypto, forex and portfolio.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
report_name: str
|
||||
Name of report chosen.
|
||||
arg_name: str
|
||||
Argument to limit choices.
|
||||
|
||||
Returns:
|
||||
List[str]: List with argument choices from autocompletion.
|
||||
"""
|
||||
|
||||
choices = None
|
||||
if report_name in ("forex", "portfolio"):
|
||||
if "--" + arg_name in REPORT_CHOICES[report_name]: # type: ignore
|
||||
choices = list(REPORT_CHOICES[report_name]["--" + arg_name].keys()) # type: ignore
|
||||
return choices
|
||||
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def get_reports_available(
|
||||
folder: Path = REPORTS_FOLDER, warn: bool = True
|
||||
) -> List[str]:
|
||||
"""Get Jupyter notebook available in folder.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
folder: Path
|
||||
Path to folder.
|
||||
|
||||
Returns:
|
||||
List[str]: List with names of notebooks available.
|
||||
"""
|
||||
|
||||
bad_format = []
|
||||
available = []
|
||||
|
||||
for notebook in os.listdir(folder):
|
||||
if notebook.endswith(".ipynb"):
|
||||
if " " in notebook:
|
||||
bad_format.append(notebook)
|
||||
else:
|
||||
available.append(notebook[:-6])
|
||||
|
||||
if bad_format and warn:
|
||||
s = ", ".join(bad_format)
|
||||
console.print(
|
||||
f"[red]Character '_' not allowed in the following names: {s}.[/red]"
|
||||
)
|
||||
|
||||
return available
|
||||
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def extract_parameters(input_path: str) -> Dict[str, str]:
|
||||
"""Extract required parameters from notebook content.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
input_path: str
|
||||
Path of report to be rendered.
|
||||
|
||||
Returns:
|
||||
Dict[str, str]: Dictionary with parameters names and values.
|
||||
"""
|
||||
|
||||
input_path = add_ipynb_extension(input_path)
|
||||
|
||||
with open(input_path, encoding="utf-8") as file:
|
||||
notebook_content = file.read()
|
||||
|
||||
# Look for the metadata cell to understand if there are parameters required by the report
|
||||
metadata_cell = """"metadata": {\n "tags": [\n "parameters"\n ]\n },\n "outputs":"""
|
||||
|
||||
# Locate position of the data of interest and get parameters
|
||||
if notebook_content.find(metadata_cell) >= 0:
|
||||
position = notebook_content.find(metadata_cell)
|
||||
else:
|
||||
return {}
|
||||
|
||||
metadata = notebook_content[position:] # noqa: E203
|
||||
cell_start = 'source": '
|
||||
cell_end = "]"
|
||||
start_position = metadata.find(cell_start)
|
||||
params = metadata[
|
||||
start_position : metadata.find(cell_end, start_position) + 1 # noqa: E203
|
||||
]
|
||||
|
||||
# Make sure that the parameters provided are relevant
|
||||
if "parameters" in notebook_content:
|
||||
parameters_names = [
|
||||
param.split("=")[0][:-1]
|
||||
for param in literal_eval(params.strip('source": '))
|
||||
if param[0] not in ["#", "\n"]
|
||||
]
|
||||
parameters_values = [
|
||||
param.split("=")[1][2:-2]
|
||||
for param in literal_eval(params.strip('source": '))
|
||||
if param[0] not in ["#", "\n"]
|
||||
]
|
||||
|
||||
if "report_name" in parameters_names:
|
||||
parameters_names.remove("report_name")
|
||||
|
||||
parameters_dict = dict(zip(parameters_names, parameters_values))
|
||||
|
||||
return parameters_dict
|
||||
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def render_report(input_path: str, args_dict: Dict[str, str]):
|
||||
"""Report rendering end to end.
|
||||
|
||||
Workflow:
|
||||
1. Update parameters to use in notebook with received arguments
|
||||
2. Create output path
|
||||
3. Update parameters with output_path
|
||||
4. Validate and execute notebook in a new thread.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
input_path: str
|
||||
Path of report to be rendered.
|
||||
args_dict: Dict[str, str]
|
||||
Dictionary with received arguments dictionary.
|
||||
"""
|
||||
|
||||
try:
|
||||
parameters_dict = update_parameters(input_path, args_dict)
|
||||
output_path = create_output_path(input_path, parameters_dict)
|
||||
parameters_dict["report_name"] = output_path
|
||||
if parameters_dict:
|
||||
t = Thread(
|
||||
target=execute_notebook, args=(input_path, parameters_dict, output_path)
|
||||
)
|
||||
t.start()
|
||||
t.join()
|
||||
except Exception as e:
|
||||
console.print(f"[red]Cannot execute notebook - {e}")
|
||||
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def update_parameters(input_path: str, args_dict: Dict[str, str]) -> Dict[str, Any]:
|
||||
"""Update dictionary of parameters to be used in report with received arguments.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
input_path: str
|
||||
Path of report to be rendered.
|
||||
args_dict: Dict[str, str]
|
||||
Dictionary with received arguments dictionary.
|
||||
|
||||
Returns
|
||||
-------
|
||||
Dict[str, Any]
|
||||
Dictionary with report parameters.
|
||||
"""
|
||||
|
||||
parameters_dict = extract_parameters(input_path)
|
||||
for key, val in args_dict.items():
|
||||
if key in parameters_dict:
|
||||
parameters_dict[key] = val
|
||||
else:
|
||||
console.print(f"[red]'{key}' not found in notebook parameters.[/red]")
|
||||
|
||||
return parameters_dict
|
||||
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def create_output_path(input_path: str, parameters_dict: Dict[str, Any]) -> str:
|
||||
"""Create path to save rendered report, thus the output path.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
input_path: str
|
||||
Path of report to be rendered.
|
||||
parameters_dict: Dict[str, Any]
|
||||
Dictionary with report parameters.
|
||||
|
||||
Returns
|
||||
-------
|
||||
str
|
||||
Path of rendered report.
|
||||
"""
|
||||
|
||||
report_name = os.path.split(input_path)[-1]
|
||||
param_values = list(parameters_dict.values())
|
||||
args_to_output = f"_{'_'.join(param_values)}" if "_".join(param_values) else ""
|
||||
report_output_name = (
|
||||
f"{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
||||
+ "_"
|
||||
+ f"{report_name}{args_to_output}"
|
||||
)
|
||||
output_path = str(USER_REPORTS_DIRECTORY / report_output_name)
|
||||
output_path = output_path.replace(".", "_")
|
||||
|
||||
return output_path
|
||||
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def execute_notebook(input_path, parameters, output_path):
|
||||
"""Execute the input path's notebook with the parameters provided.
|
||||
Then, save it in the output path.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
input_path: str
|
||||
Path of report to be rendered.
|
||||
parameters: Dict[str, Any]
|
||||
Dictionary with report parameters.
|
||||
output_path: str
|
||||
Path of rendered report.
|
||||
"""
|
||||
|
||||
input_path = add_ipynb_extension(input_path)
|
||||
|
||||
try:
|
||||
result = pm.execute_notebook(
|
||||
input_path=input_path,
|
||||
output_path=output_path + ".ipynb",
|
||||
parameters=parameters,
|
||||
)
|
||||
|
||||
if not result["metadata"]["papermill"]["exception"]:
|
||||
console.print(f"\n[green]Notebook:[/green] {output_path}.ipynb")
|
||||
if obbff.OPEN_REPORT_AS_HTML:
|
||||
report_output_path = os.path.join(
|
||||
os.path.abspath(os.path.join(".")), output_path + ".html"
|
||||
)
|
||||
webbrowser.open(f"file://{report_output_path}")
|
||||
console.print(f"\n[green]Report:[/green] {report_output_path}\n")
|
||||
else:
|
||||
console.print("\n")
|
||||
else:
|
||||
console.print("[red]\nReport .html couldn't be created.\n[/red]")
|
||||
|
||||
except pm.PapermillExecutionError as e:
|
||||
console.print(
|
||||
f"[red]An error was encountered in cell [{e.cell_index}], check the notebook:[/red]\n"
|
||||
f"{output_path}\n"
|
||||
)
|
||||
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def add_ipynb_extension(path: str) -> str:
|
||||
"""Add .ipynb extension to path.
|
||||
Parameters
|
||||
----------
|
||||
path: str
|
||||
Path to notebook file.
|
||||
|
||||
Returns
|
||||
-------
|
||||
str
|
||||
Path to .ipynb file.
|
||||
"""
|
||||
|
||||
if not path.endswith(".ipynb"):
|
||||
return path + ".ipynb"
|
||||
return path
|
||||
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def check_ipynb(path: str) -> str:
|
||||
"""Check if there is .ipynb extension in path.
|
||||
This is useful to the controller type check.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
path: str
|
||||
Path to notebook file.
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
Path if paths endswith .ipynb, else empty string.
|
||||
"""
|
||||
|
||||
if not path.endswith(".ipynb"):
|
||||
console.print("[red]Please provide a .ipynb file.[/red]\n")
|
||||
return ""
|
||||
return path
|
||||
|
||||
|
||||
def ipykernel_launcher(module_file: str, module_hist_file: str):
|
||||
"""This function mocks 'ipykernel_launcher.py' launching a Jupyter notebook kernel.
|
||||
|
||||
It is useful when running python commands inside a frozen application like our
|
||||
installer distribution, where sys.executable[0] is not the path to python
|
||||
interpreter, rather it is the path to the application executable.
|
||||
|
||||
Problem:
|
||||
'papermill' was trying to execute the following command on a subprocess:
|
||||
$ .../bin/python -m ipykernel_launcher -f ... --HistoryManager.hist_file ...
|
||||
|
||||
'papermill' was using '.../bin/python' because it is looks for the sys.executable[0],
|
||||
which most of the time leads to the python interpreter. In our frozen app,
|
||||
sys.executable[0] leads to 'OpenBB Terminal/.OpenBB/OpenBBTerminal', which in turn
|
||||
executes 'terminal.py.
|
||||
|
||||
This means that the command was being executed in 'terminal.py'. Consequently,
|
||||
one gets the following error message:
|
||||
$ terminal: error: unrecognized arguments: -m ipykernel_launcher -f ... --HistoryManager.hist_file ...
|
||||
|
||||
Solution:
|
||||
Parse 'papermill' command in the 'terminal_controller', which is what follows
|
||||
'terminal.py' and here receive the parsed 'papermill' command arguments and
|
||||
route them to IPKernelApp as if this is 'ipykernel_launcher' module
|
||||
- the kernel is launched.
|
||||
|
||||
Source: https://pyinstaller.org/en/stable/runtime-information.html#using-sys-executable-and-sys-argv-0
|
||||
|
||||
Parameters
|
||||
----------
|
||||
module_file: str
|
||||
Specified connection file.
|
||||
module_hist_file: str
|
||||
History manager file.
|
||||
"""
|
||||
|
||||
IPKernelApp.launch_instance(
|
||||
argv=[
|
||||
"-f",
|
||||
module_file,
|
||||
"--HistoryManager.hist_file",
|
||||
module_hist_file,
|
||||
]
|
||||
)
|
||||
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
4231
openbb_terminal/reports/templates/crypto.ipynb
vendored
Normal file
4231
openbb_terminal/reports/templates/crypto.ipynb
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -34,7 +34,7 @@
|
||||
"from openbb_terminal.sdk import helper\n",
|
||||
"from openbb_terminal.sdk import openbb\n",
|
||||
"from openbb_terminal.helper_classes import TerminalStyle\n",
|
||||
"\n",
|
||||
"from openbb_terminal.core.config.paths import REPOSITORY_DIRECTORY\n",
|
||||
"\n",
|
||||
"%matplotlib inline\n",
|
||||
"matplotlib_inline.backend_inline.set_matplotlib_formats(\"svg\")\n",
|
||||
@@ -103,7 +103,7 @@
|
||||
"id": "0b9ec3f9",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Overview"
|
||||
"Summary"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -171,24 +171,27 @@
|
||||
" start_date=datetime.datetime.now().strftime(\"%Y-%m-%d\"),\n",
|
||||
" importance=\"high\",\n",
|
||||
")\n",
|
||||
"events_1 = events_1.set_index(\"Date\")\n",
|
||||
"if not events_1.empty:\n",
|
||||
" events_1 = events_1.set_index(\"Date\")\n",
|
||||
"\n",
|
||||
"events_2, title_2 = openbb.economy.events(\n",
|
||||
" country=\"Euro zone\",\n",
|
||||
" start_date=datetime.datetime.now().strftime(\"%Y-%m-%d\"),\n",
|
||||
" importance=\"all\",\n",
|
||||
")\n",
|
||||
"events_2 = events_2[\n",
|
||||
" (events_2[\"Importance\"] == \"High\") | (events_2[\"Importance\"] == \"Medium\")\n",
|
||||
"]\n",
|
||||
"events_2 = events_2.set_index(\"Date\")\n",
|
||||
"if not events_2.empty:\n",
|
||||
" events_2 = events_2[\n",
|
||||
" (events_2[\"Importance\"] == \"High\") | (events_2[\"Importance\"] == \"Medium\")\n",
|
||||
" ]\n",
|
||||
" events_2 = events_2.set_index(\"Date\")\n",
|
||||
"\n",
|
||||
"events_3, title_3 = openbb.economy.events(\n",
|
||||
" country=\"China\",\n",
|
||||
" start_date=datetime.datetime.now().strftime(\"%Y-%m-%d\"),\n",
|
||||
" importance=\"all\",\n",
|
||||
")\n",
|
||||
"events_3 = events_3.set_index(\"Date\")"
|
||||
"if not events_3.empty:\n",
|
||||
" events_3 = events_3.set_index(\"Date\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -223,20 +226,21 @@
|
||||
"fig.savefig(f, format=\"svg\")\n",
|
||||
"ycrv_chart_1 = f.getvalue().decode(\"utf-8\")\n",
|
||||
"\n",
|
||||
"ycrv_data_1.set_index(\"Tenor\", inplace=True)\n",
|
||||
"ycrv_data_1.index = [\n",
|
||||
" \"1M\",\n",
|
||||
" \"3M\",\n",
|
||||
" \"6M\",\n",
|
||||
" \"1Y\",\n",
|
||||
" \"2Y\",\n",
|
||||
" \"3Y\",\n",
|
||||
" \"5Y\",\n",
|
||||
" \"7Y\",\n",
|
||||
" \"10Y\",\n",
|
||||
" \"20Y\",\n",
|
||||
" \"30Y\",\n",
|
||||
"]"
|
||||
"if not ycrv_data_1.empty:\n",
|
||||
" ycrv_data_1.set_index(\"Tenor\", inplace=True)\n",
|
||||
" ycrv_data_1.index = [\n",
|
||||
" \"1M\",\n",
|
||||
" \"3M\",\n",
|
||||
" \"6M\",\n",
|
||||
" \"1Y\",\n",
|
||||
" \"2Y\",\n",
|
||||
" \"3Y\",\n",
|
||||
" \"5Y\",\n",
|
||||
" \"7Y\",\n",
|
||||
" \"10Y\",\n",
|
||||
" \"20Y\",\n",
|
||||
" \"30Y\",\n",
|
||||
" ]"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -262,26 +266,27 @@
|
||||
"f = io.BytesIO()\n",
|
||||
"fig.savefig(f, format=\"svg\")\n",
|
||||
"ycrv_chart_2 = f.getvalue().decode(\"utf-8\")\n",
|
||||
"ycrv_data_2.set_index(\"Tenor\", inplace=True)\n",
|
||||
"ycrv_data_2.index = [\n",
|
||||
" \"3M\",\n",
|
||||
" \"6M\",\n",
|
||||
" \"9M\",\n",
|
||||
" \"1Y\",\n",
|
||||
" \"2Y\",\n",
|
||||
" \"3Y\",\n",
|
||||
" \"4Y\",\n",
|
||||
" \"5Y\",\n",
|
||||
" \"6Y\",\n",
|
||||
" \"7Y\",\n",
|
||||
" \"8Y\",\n",
|
||||
" \"9Y\",\n",
|
||||
" \"10Y\",\n",
|
||||
" \"15Y\",\n",
|
||||
" \"20Y\",\n",
|
||||
" \"25Y\",\n",
|
||||
" \"30Y\",\n",
|
||||
"]"
|
||||
"if not ycrv_data_1.empty:\n",
|
||||
" ycrv_data_2.set_index(\"Tenor\", inplace=True)\n",
|
||||
" ycrv_data_2.index = [\n",
|
||||
" \"3M\",\n",
|
||||
" \"6M\",\n",
|
||||
" \"9M\",\n",
|
||||
" \"1Y\",\n",
|
||||
" \"2Y\",\n",
|
||||
" \"3Y\",\n",
|
||||
" \"4Y\",\n",
|
||||
" \"5Y\",\n",
|
||||
" \"6Y\",\n",
|
||||
" \"7Y\",\n",
|
||||
" \"8Y\",\n",
|
||||
" \"9Y\",\n",
|
||||
" \"10Y\",\n",
|
||||
" \"15Y\",\n",
|
||||
" \"20Y\",\n",
|
||||
" \"25Y\",\n",
|
||||
" \"30Y\",\n",
|
||||
" ]"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -663,8 +668,8 @@
|
||||
"source": [
|
||||
"body = \"\"\n",
|
||||
"\n",
|
||||
"img = \"./openbb_terminal/reports/OpenBB_reports_logo.png\"\n",
|
||||
"floppy_disk_img = \"./openbb_terminal/reports/floppy-disc.png\"\n",
|
||||
"img = str(REPOSITORY_DIRECTORY) + \"/openbb_terminal/reports/templates/OpenBB_reports_logo.png\"\n",
|
||||
"floppy_disk_img = str(REPOSITORY_DIRECTORY) + \"/openbb_terminal/reports/templates/floppy-disc.png\"\n",
|
||||
"body += widgets.header(\n",
|
||||
" img,\n",
|
||||
" floppy_disk_img,\n",
|
||||
@@ -677,7 +682,7 @@
|
||||
"\n",
|
||||
"body += widgets.tablinks(\n",
|
||||
" [\n",
|
||||
" \"Overview\",\n",
|
||||
" \"SUMMARY\",\n",
|
||||
" \"Events\",\n",
|
||||
" \"Yields\",\n",
|
||||
" \"Equity Market\",\n",
|
||||
@@ -685,7 +690,7 @@
|
||||
" ]\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Overview\n",
|
||||
"# Summary\n",
|
||||
"htmlcode = widgets.row(\n",
|
||||
" [widgets.h(3, \"Global markets (source: WSJ)\") + overview.to_html()]\n",
|
||||
")\n",
|
||||
@@ -704,7 +709,7 @@
|
||||
" + news[:6].to_html()\n",
|
||||
" ]\n",
|
||||
")\n",
|
||||
"body += widgets.add_tab(\"Overview\", htmlcode)\n",
|
||||
"body += widgets.add_tab(\"SUMMARY\", htmlcode)\n",
|
||||
"\n",
|
||||
"# Events\n",
|
||||
"htmlcode = widgets.row([widgets.h(3, \"Economic calendars (source: investing.com)\")])\n",
|
||||
@@ -762,20 +767,12 @@
|
||||
"with open(report_name + \".html\", \"w\", encoding=\"utf-8\") as fh:\n",
|
||||
" fh.write(report)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "44d43dde-189c-4371-aa14-e72c428de830",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"celltoolbar": "Tags",
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3.10.4 ('obb')",
|
||||
"display_name": "Python 3.9.13 ('obb')",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
@@ -789,11 +786,11 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.4"
|
||||
"version": "3.9.13"
|
||||
},
|
||||
"vscode": {
|
||||
"interpreter": {
|
||||
"hash": "1a8cc6b6e60740679b24fc1ea93bdeb94d949a22102a80c99b7fd3f0d572afd6"
|
||||
"hash": "e784374cc4f9a84ed6af2983247aaf351b155c82bcc2188134a65f3c99031000"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -35,7 +35,7 @@
|
||||
"from openbb_terminal.sdk import openbb\n",
|
||||
"from openbb_terminal import config_terminal as cfg\n",
|
||||
"from openbb_terminal.helper_classes import TerminalStyle\n",
|
||||
"from openbb_terminal.core.config.paths import USER_ENV_FILE\n",
|
||||
"from openbb_terminal.core.config.paths import USER_ENV_FILE, REPOSITORY_DIRECTORY\n",
|
||||
"\n",
|
||||
"%matplotlib inline\n",
|
||||
"matplotlib_inline.backend_inline.set_matplotlib_formats(\"svg\")\n",
|
||||
@@ -88,7 +88,7 @@
|
||||
"# Parameters that will be replaced when calling this notebook\n",
|
||||
"# Do not leave parameters blank as notebook will not run otherwise\n",
|
||||
"symbol = \"msft\"\n",
|
||||
"report_name = \"Equity report for AAPL\""
|
||||
"report_name = f\"Equity report for {symbol}\""
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -133,6 +133,7 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"info = openbb.stocks.fa.info(symbol=symbol).transpose()\n",
|
||||
"\n",
|
||||
"if info[\"Long business summary\"][0] != \"NA\":\n",
|
||||
" overview = info[\"Long business summary\"][0]\n",
|
||||
"else:\n",
|
||||
@@ -181,10 +182,6 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"tables = openbb.etf.news(info[\"Short name\"][0], 5)\n",
|
||||
"\n",
|
||||
"# Add an empty iterator if it the returned type is None\n",
|
||||
"# i.e.: in case of lack of a valid API\n",
|
||||
"tables = tables if tables is not None else []\n",
|
||||
"for table in tables:\n",
|
||||
" table[0].loc[\"link\"] = (\n",
|
||||
" table[0].loc[\"link\"].apply(lambda x: f'<a href=\"{x}\">{x}</a>')\n",
|
||||
@@ -535,10 +532,6 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"similar_companies = openbb.stocks.ca.polygon_peers(symbol)\n",
|
||||
"\n",
|
||||
"# Add an empty list if it the returned list is empty\n",
|
||||
"# i.e.: in case of lack of a valid API\n",
|
||||
"similar_companies = similar_companies if similar_companies is not None else []\n",
|
||||
"similar_companies.append(symbol)"
|
||||
]
|
||||
},
|
||||
@@ -1231,8 +1224,8 @@
|
||||
"source": [
|
||||
"body = \"\"\n",
|
||||
"\n",
|
||||
"img = \"./openbb_terminal/reports/OpenBB_reports_logo.png\"\n",
|
||||
"floppy_disk_img = \"./openbb_terminal/reports/floppy-disc.png\"\n",
|
||||
"img = str(REPOSITORY_DIRECTORY) + \"/openbb_terminal/reports/templates/OpenBB_reports_logo.png\"\n",
|
||||
"floppy_disk_img = str(REPOSITORY_DIRECTORY) + \"/openbb_terminal/reports/templates/floppy-disc.png\"\n",
|
||||
"body += widgets.header(\n",
|
||||
" img,\n",
|
||||
" floppy_disk_img,\n",
|
||||
@@ -1245,7 +1238,7 @@
|
||||
"\n",
|
||||
"body += widgets.tablinks(\n",
|
||||
" [\n",
|
||||
" \"Summary\",\n",
|
||||
" \"SUMMARY\",\n",
|
||||
" \"Overview\",\n",
|
||||
" \"Fundamental Analysis\",\n",
|
||||
" \"Technical Analysis\",\n",
|
||||
@@ -1320,7 +1313,7 @@
|
||||
" ],\n",
|
||||
" round(regression_val - last_price, 2),\n",
|
||||
" )\n",
|
||||
"body += widgets.add_tab(\"Summary\", htmlcode)\n",
|
||||
"body += widgets.add_tab(\"SUMMARY\", htmlcode)\n",
|
||||
"\n",
|
||||
"htmlcode = widgets.row([widgets.h(3, \"Description\") + widgets.p(overview)])\n",
|
||||
"htmlcode += widgets.row([widgets.h(3, \"Price Chart\") + price_chart])\n",
|
||||
@@ -1382,13 +1375,9 @@
|
||||
" )\n",
|
||||
"except:\n",
|
||||
" pass\n",
|
||||
"\n",
|
||||
"try:\n",
|
||||
" htmlcode += widgets.row(\n",
|
||||
" [widgets.h(3, f\"Enterprise data for {symbol}\") + enterprise_df.to_html()]\n",
|
||||
" )\n",
|
||||
"except:\n",
|
||||
" pass\n",
|
||||
"htmlcode += widgets.row(\n",
|
||||
" [widgets.h(3, f\"Enterprise data for {symbol}\") + enterprise_df.to_html()]\n",
|
||||
")\n",
|
||||
"htmlcode += widgets.row(\n",
|
||||
" [widgets.h(3, f\"Income Statement for {symbol}\") + income_df.to_html()]\n",
|
||||
")\n",
|
||||
@@ -1402,7 +1391,9 @@
|
||||
" )\n",
|
||||
"except:\n",
|
||||
" pass\n",
|
||||
"\n",
|
||||
"body += widgets.add_tab(\"Fundamental Analysis\", htmlcode)\n",
|
||||
"\n",
|
||||
"htmlcode = widgets.row([widgets.h(3, \"Put to call ratio\") + pcr_chart])\n",
|
||||
"htmlcode += widgets.row(\n",
|
||||
" [widgets.h(3, \"Option Volume for closest expiry date\") + vol_chart]\n",
|
||||
@@ -1570,14 +1561,6 @@
|
||||
" os.getenv(\"OPENBB_RICHSTYLE\") or \"dark\",\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "f20745a4-053f-4cd3-8bb2-96c88c0600df",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
@@ -1597,11 +1580,11 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.4"
|
||||
"version": "3.9.13"
|
||||
},
|
||||
"vscode": {
|
||||
"interpreter": {
|
||||
"hash": "1a8cc6b6e60740679b24fc1ea93bdeb94d949a22102a80c99b7fd3f0d572afd6"
|
||||
"hash": "e784374cc4f9a84ed6af2983247aaf351b155c82bcc2188134a65f3c99031000"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -33,6 +33,7 @@
|
||||
"from openbb_terminal.sdk import widgets\n",
|
||||
"from openbb_terminal.sdk import openbb\n",
|
||||
"from openbb_terminal.helper_classes import TerminalStyle\n",
|
||||
"from openbb_terminal.core.config.paths import REPOSITORY_DIRECTORY\n",
|
||||
"\n",
|
||||
"%matplotlib inline\n",
|
||||
"matplotlib_inline.backend_inline.set_matplotlib_formats(\"svg\")\n",
|
||||
@@ -62,12 +63,7 @@
|
||||
" theme = TerminalStyle(\"light\", \"light\", \"light\")\n",
|
||||
"except:\n",
|
||||
" pass\n",
|
||||
"stylesheet = widgets.html_report_stylesheet()\n",
|
||||
"try:\n",
|
||||
" with open(\"openbb_terminal/reports/OpenBB_reports_logo.png\", \"rb\") as image_file:\n",
|
||||
" openbb_image_encoded = base64.b64encode(image_file.read())\n",
|
||||
"except FileNotFoundError:\n",
|
||||
" openbb_image_encoded = None"
|
||||
"stylesheet = widgets.html_report_stylesheet()"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -240,8 +236,8 @@
|
||||
"source": [
|
||||
"body = \"\"\n",
|
||||
"\n",
|
||||
"img = \"./openbb_terminal/reports/OpenBB_reports_logo.png\"\n",
|
||||
"floppy_disk_img = (\"./openbb_terminal/reports/floppy-disc.png\",)\n",
|
||||
"img = str(REPOSITORY_DIRECTORY) + \"/openbb_terminal/reports/templates/OpenBB_reports_logo.png\"\n",
|
||||
"floppy_disk_img = str(REPOSITORY_DIRECTORY) + \"/openbb_terminal/reports/templates/floppy-disc.png\"\n",
|
||||
"body += widgets.header(\n",
|
||||
" img,\n",
|
||||
" floppy_disk_img,\n",
|
||||
@@ -275,20 +271,12 @@
|
||||
"with open(report_name + \".html\", \"w\", encoding=\"utf-8\") as fh:\n",
|
||||
" fh.write(report)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "703368dd-a57d-4584-a85c-319ead55f628",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"celltoolbar": "Tags",
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"display_name": "Python 3.9.13 ('obb')",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
@@ -302,11 +290,11 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9.6"
|
||||
"version": "3.9.13"
|
||||
},
|
||||
"vscode": {
|
||||
"interpreter": {
|
||||
"hash": "ab0e87f2bc73919b75956d1b3e303320699d53da895be1bb46287d7dc67c6497"
|
||||
"hash": "e784374cc4f9a84ed6af2983247aaf351b155c82bcc2188134a65f3c99031000"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
@@ -30,6 +30,7 @@
|
||||
"from openbb_terminal.sdk import widgets\n",
|
||||
"from openbb_terminal.sdk import openbb\n",
|
||||
"from openbb_terminal.helper_classes import TerminalStyle\n",
|
||||
"from openbb_terminal.core.config.paths import REPOSITORY_DIRECTORY\n",
|
||||
"\n",
|
||||
"%matplotlib inline\n",
|
||||
"matplotlib_inline.backend_inline.set_matplotlib_formats(\"svg\")\n",
|
||||
@@ -47,12 +48,7 @@
|
||||
" theme = TerminalStyle(\"light\", \"light\", \"light\")\n",
|
||||
"except:\n",
|
||||
" pass\n",
|
||||
"stylesheet = widgets.html_report_stylesheet()\n",
|
||||
"try:\n",
|
||||
" with open(\"openbb_terminal/reports/OpenBB_reports_logo.png\", \"rb\") as image_file:\n",
|
||||
" openbb_image_encoded = base64.b64encode(image_file.read())\n",
|
||||
"except FileNotFoundError:\n",
|
||||
" openbb_image_encoded = None"
|
||||
"stylesheet = widgets.html_report_stylesheet()"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -230,8 +226,8 @@
|
||||
"source": [
|
||||
"body = \"\"\n",
|
||||
"\n",
|
||||
"img = \"./openbb_terminal/reports/OpenBB_reports_logo.png\"\n",
|
||||
"floppy_disk_img = (\"./openbb_terminal/reports/floppy-disc.png\",)\n",
|
||||
"img = str(REPOSITORY_DIRECTORY) + \"/openbb_terminal/reports/templates/OpenBB_reports_logo.png\"\n",
|
||||
"floppy_disk_img = str(REPOSITORY_DIRECTORY) + \"/openbb_terminal/reports/templates/floppy-disc.png\"\n",
|
||||
"body += widgets.header(\n",
|
||||
" img,\n",
|
||||
" floppy_disk_img,\n",
|
||||
@@ -260,20 +256,12 @@
|
||||
"with open(report_name + \".html\", \"w\", encoding=\"utf-8\") as fh:\n",
|
||||
" fh.write(report)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "b7f52d81-8362-4a68-9830-fee24c16fa08",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"celltoolbar": "Tags",
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3.10.4 ('obb')",
|
||||
"display_name": "Python 3.9.13 ('obb')",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
@@ -287,11 +275,11 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.4"
|
||||
"version": "3.9.13"
|
||||
},
|
||||
"vscode": {
|
||||
"interpreter": {
|
||||
"hash": "1a8cc6b6e60740679b24fc1ea93bdeb94d949a22102a80c99b7fd3f0d572afd6"
|
||||
"hash": "e784374cc4f9a84ed6af2983247aaf351b155c82bcc2188134a65f3c99031000"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -34,6 +34,7 @@
|
||||
"from openbb_terminal.sdk import widgets\n",
|
||||
"from openbb_terminal.sdk import openbb\n",
|
||||
"from openbb_terminal.helper_classes import TerminalStyle\n",
|
||||
"from openbb_terminal.core.config.paths import REPOSITORY_DIRECTORY\n",
|
||||
"\n",
|
||||
"%matplotlib inline\n",
|
||||
"matplotlib_inline.backend_inline.set_matplotlib_formats(\"svg\")\n",
|
||||
@@ -51,12 +52,7 @@
|
||||
" theme = TerminalStyle(\"light\", \"light\", \"light\")\n",
|
||||
"except:\n",
|
||||
" pass\n",
|
||||
"stylesheet = widgets.html_report_stylesheet()\n",
|
||||
"try:\n",
|
||||
" with open(\"./openbb_terminal/reports/OpenBB_reports_logo.png\", \"rb\") as image_file:\n",
|
||||
" openbb_image_encoded = base64.b64encode(image_file.read())\n",
|
||||
"except FileNotFoundError:\n",
|
||||
" openbb_image_encoded = None"
|
||||
"stylesheet = widgets.html_report_stylesheet()"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -630,11 +626,8 @@
|
||||
"source": [
|
||||
"body = \"\"\n",
|
||||
"\n",
|
||||
"try:\n",
|
||||
" img = f'<img src=\"data:image/png;base64,{openbb_image_encoded.decode()}\" alt=\"OpenBB\" style=\"width:144px;\">'\n",
|
||||
"except AttributeError:\n",
|
||||
" img = None\n",
|
||||
"floppy_disk_img = \"openbb_terminal/reports/floppy-disc.png\"\n",
|
||||
"img = str(REPOSITORY_DIRECTORY) + \"/openbb_terminal/reports/templates/OpenBB_reports_logo.png\"\n",
|
||||
"floppy_disk_img = str(REPOSITORY_DIRECTORY) + \"/openbb_terminal/reports/templates/floppy-disc.png\"\n",
|
||||
"body += widgets.header(\n",
|
||||
" img,\n",
|
||||
" floppy_disk_img,\n",
|
||||
@@ -757,7 +750,7 @@
|
||||
"metadata": {
|
||||
"celltoolbar": "Tags",
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3.10.4 ('obb')",
|
||||
"display_name": "Python 3.9.13 ('obb')",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
@@ -771,11 +764,11 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.4"
|
||||
"version": "3.9.13"
|
||||
},
|
||||
"vscode": {
|
||||
"interpreter": {
|
||||
"hash": "1a8cc6b6e60740679b24fc1ea93bdeb94d949a22102a80c99b7fd3f0d572afd6"
|
||||
"hash": "e784374cc4f9a84ed6af2983247aaf351b155c82bcc2188134a65f3c99031000"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -38,6 +38,7 @@
|
||||
"from openbb_terminal.sdk import openbb\n",
|
||||
"from openbb_terminal.sdk import Portfolio\n",
|
||||
"from openbb_terminal.helper_classes import TerminalStyle\n",
|
||||
"from openbb_terminal.core.config.paths import REPOSITORY_DIRECTORY\n",
|
||||
"\n",
|
||||
"%matplotlib inline\n",
|
||||
"matplotlib_inline.backend_inline.set_matplotlib_formats(\"svg\")\n",
|
||||
@@ -93,19 +94,6 @@
|
||||
"report_name = \"Portfolio Report\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "ee7112da",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if \"reports\" in os.getcwd():\n",
|
||||
" holdings = \"../../portfolio/holdings/\"\n",
|
||||
"else:\n",
|
||||
" holdings = \"portfolio/holdings/\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
@@ -113,6 +101,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"holdings = str(REPOSITORY_DIRECTORY) + \"/openbb_terminal/miscellaneous/portfolio_examples/holdings/\"\n",
|
||||
"orderbook_path = holdings + orderbook\n",
|
||||
"_, orderbook_file = os.path.split(orderbook_path)\n",
|
||||
"report_title = f\"Portfolio report for {orderbook_file}\"\n",
|
||||
@@ -153,7 +142,7 @@
|
||||
" P.get_orderbook()\n",
|
||||
"except ValueError:\n",
|
||||
" raise ValueError(\n",
|
||||
" \"Failed to load the orderbook. Is this file inside the 'Holdings' folder?\"\n",
|
||||
" \"Failed to load the orderbook. Is this file inside the 'holdings' folder?\"\n",
|
||||
" )"
|
||||
]
|
||||
},
|
||||
@@ -348,13 +337,10 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from glob import escape\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"body = \"\"\n",
|
||||
"\n",
|
||||
"img = \"./openbb_terminal/reports/OpenBB_reports_logo.png\"\n",
|
||||
"floppy_disk_img = (\"./openbb_terminal/reports/floppy-disc.png\",)\n",
|
||||
"img = str(REPOSITORY_DIRECTORY) + \"/openbb_terminal/reports/templates/OpenBB_reports_logo.png\"\n",
|
||||
"floppy_disk_img = str(REPOSITORY_DIRECTORY) + \"/openbb_terminal/reports/templates/floppy-disc.png\"\n",
|
||||
"body += widgets.header(\n",
|
||||
" img,\n",
|
||||
" floppy_disk_img,\n",
|
||||
@@ -365,7 +351,7 @@
|
||||
" report_title,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"body += widgets.tablinks([\"Summary\", \"In-Depth Analysis\", \"Statistics\"])\n",
|
||||
"body += widgets.tablinks([\"SUMMARY\", \"In-Depth Analysis\", \"Statistics\"])\n",
|
||||
"\n",
|
||||
"# Summary\n",
|
||||
"htmlcode = widgets.p(\n",
|
||||
@@ -409,7 +395,7 @@
|
||||
" [widgets.h(3, \"Benchmark Trades\") + P.benchmark_trades.to_html()]\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"body += widgets.add_tab(\"Summary\", htmlcode)\n",
|
||||
"body += widgets.add_tab(\"SUMMARY\", htmlcode)\n",
|
||||
"\n",
|
||||
"# In Depth Analysis\n",
|
||||
"htmlcode = widgets.p(\n",
|
||||
@@ -480,7 +466,7 @@
|
||||
"metadata": {
|
||||
"celltoolbar": "Tags",
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3.10.4 ('obb')",
|
||||
"display_name": "Python 3.9.13 ('obb')",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
@@ -494,11 +480,11 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.4"
|
||||
"version": "3.9.13"
|
||||
},
|
||||
"vscode": {
|
||||
"interpreter": {
|
||||
"hash": "1a8cc6b6e60740679b24fc1ea93bdeb94d949a22102a80c99b7fd3f0d572afd6"
|
||||
"hash": "e784374cc4f9a84ed6af2983247aaf351b155c82bcc2188134a65f3c99031000"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -7,7 +7,7 @@ import warnings
|
||||
import types
|
||||
import functools
|
||||
import importlib
|
||||
from typing import Optional, Callable, List
|
||||
from typing import Optional, Callable, List, Union
|
||||
import logging
|
||||
from traceback import format_stack
|
||||
|
||||
@@ -2383,7 +2383,7 @@ class Loader:
|
||||
log_all_settings()
|
||||
|
||||
@classmethod
|
||||
def get_function(cls, function_path: str) -> Callable:
|
||||
def get_function(cls, function_path: str) -> Union[Callable, None]:
|
||||
"""Get function from string path.
|
||||
|
||||
Parameters
|
||||
@@ -2399,7 +2399,16 @@ class Loader:
|
||||
Function
|
||||
"""
|
||||
module_path, function_name = function_path.rsplit(sep=".", maxsplit=1)
|
||||
module = cls.__load_module(module_path=module_path)
|
||||
try:
|
||||
module = cls.__load_module(module_path=module_path)
|
||||
except Exception as e:
|
||||
# This avoids crash on loading SDK under installer application.
|
||||
# SDK is used by papermill to generate reports and some dependencies
|
||||
# are not compatible. Since the reports are run in a subprocess,
|
||||
# this error message is actually not displayed on screen.
|
||||
# TODO: Fix this.
|
||||
console.print(f"Cannot load: {module_path} -> {e}")
|
||||
return None
|
||||
|
||||
try:
|
||||
function = getattr(module, function_name)
|
||||
|
||||
@@ -18,7 +18,6 @@ from prompt_toolkit.completion import NestedCompleter
|
||||
from prompt_toolkit.styles import Style
|
||||
from prompt_toolkit.formatted_text import HTML
|
||||
import pandas as pd
|
||||
|
||||
from openbb_terminal import feature_flags as obbff
|
||||
from openbb_terminal import keys_controller
|
||||
from openbb_terminal.terminal_helper import is_packaged_application
|
||||
@@ -40,7 +39,7 @@ from openbb_terminal.helper_funcs import (
|
||||
)
|
||||
from openbb_terminal.loggers import setup_logging
|
||||
from openbb_terminal.core.log.generation.settings_logger import log_all_settings
|
||||
from openbb_terminal.menu import session
|
||||
from openbb_terminal.menu import session, is_papermill
|
||||
from openbb_terminal.parent_classes import BaseController
|
||||
from openbb_terminal.rich_config import console, MenuText
|
||||
from openbb_terminal.terminal_helper import (
|
||||
@@ -55,8 +54,9 @@ from openbb_terminal.terminal_helper import (
|
||||
)
|
||||
from openbb_terminal.helper_funcs import parse_and_split_input
|
||||
from openbb_terminal.common import feedparser_view
|
||||
from openbb_terminal.reports.reports_model import ipykernel_launcher
|
||||
|
||||
# pylint: disable=too-many-public-methods,import-outside-toplevel
|
||||
# pylint: disable=too-many-public-methods,import-outside-toplevel, too-many-function-args
|
||||
# pylint: disable=too-many-branches,no-member,C0302,too-many-return-statements
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -889,8 +889,10 @@ def terminal(jobs_cmds: List[str] = None, test_mode=False):
|
||||
except (KeyboardInterrupt, EOFError):
|
||||
print_goodbye()
|
||||
break
|
||||
# Get input from user without auto-completion
|
||||
elif is_papermill():
|
||||
pass
|
||||
else:
|
||||
# Get input from user without auto-completion
|
||||
an_input = input(f"{get_flair()} / $ ")
|
||||
|
||||
try:
|
||||
@@ -1098,6 +1100,7 @@ def main(
|
||||
path_list: List[str],
|
||||
verbose: bool,
|
||||
routines_args: List[str] = None,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
Runs the terminal with various options
|
||||
@@ -1119,12 +1122,16 @@ def main(
|
||||
E.g. GME,AMC,BTC-USD
|
||||
"""
|
||||
|
||||
if kwargs["module"] == "ipykernel_launcher":
|
||||
ipykernel_launcher(kwargs["module_file"], kwargs["module_hist_file"])
|
||||
|
||||
if test:
|
||||
run_test_list(path_list=path_list, filtert=filtert, verbose=verbose)
|
||||
return
|
||||
|
||||
if debug:
|
||||
os.environ["DEBUG_MODE"] = "true"
|
||||
|
||||
if isinstance(path_list, list) and path_list[0].endswith(".openbb"):
|
||||
run_routine(file=path_list[0], routines_args=routines_args)
|
||||
elif path_list:
|
||||
@@ -1186,10 +1193,41 @@ def parse_args_and_run():
|
||||
type=lambda s: [str(item) for item in s.split(",")],
|
||||
default=None,
|
||||
)
|
||||
|
||||
# The args -m, -f and --HistoryManager.hist_file are used only in reports menu
|
||||
# by papermill and that's why they have suppress help.
|
||||
parser.add_argument(
|
||||
"-m",
|
||||
help=argparse.SUPPRESS,
|
||||
dest="module",
|
||||
default="",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-f",
|
||||
help=argparse.SUPPRESS,
|
||||
dest="module_file",
|
||||
default="",
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--HistoryManager.hist_file",
|
||||
help=argparse.SUPPRESS,
|
||||
dest="module_hist_file",
|
||||
default="",
|
||||
type=str,
|
||||
)
|
||||
if sys.argv[1:] and "-" not in sys.argv[1][0]:
|
||||
sys.argv.insert(1, "--file")
|
||||
ns_parser = parser.parse_args()
|
||||
ns_parser, unknown = parser.parse_known_args()
|
||||
|
||||
# This ensures that if terminal.py receives unknown args it will not start.
|
||||
# Use -d flag if you want to see the unknown args.
|
||||
if unknown:
|
||||
if ns_parser.debug:
|
||||
console.print(unknown)
|
||||
else:
|
||||
sys.exit(-1)
|
||||
|
||||
main(
|
||||
ns_parser.debug,
|
||||
ns_parser.test,
|
||||
@@ -1197,6 +1235,9 @@ def parse_args_and_run():
|
||||
ns_parser.path,
|
||||
ns_parser.verbose,
|
||||
ns_parser.routine_args,
|
||||
module=ns_parser.module,
|
||||
module_file=ns_parser.module_file,
|
||||
module_hist_file=ns_parser.module_hist_file,
|
||||
)
|
||||
|
||||
|
||||
|
||||
2614
poetry.lock
generated
vendored
2614
poetry.lock
generated
vendored
File diff suppressed because it is too large
Load Diff
1
pyproject.toml
vendored
1
pyproject.toml
vendored
@@ -100,6 +100,7 @@ ipyflex = "^0.2.4"
|
||||
setuptools = "^65.4.1"
|
||||
investiny = "^0.5.0"
|
||||
numpy = "1.21.6"
|
||||
papermill = {git = "https://github.com/nteract/papermill.git", rev = "main"}
|
||||
stocksera = "^0.1.21"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
|
||||
7
requirements-full.txt
vendored
7
requirements-full.txt
vendored
@@ -38,7 +38,7 @@ cfgv==3.3.1; python_full_version >= "3.6.1" and python_version >= "3.7"
|
||||
charset-normalizer==2.1.0; python_full_version >= "3.6.0"
|
||||
click==8.1.3; python_version >= "3.7" and python_full_version < "3.9.7" or python_full_version > "3.9.7" and python_version >= "3.7"
|
||||
codespell==2.2.2; python_version >= "3.7"
|
||||
colorama==0.4.6; python_full_version >= "3.7.2" and python_version >= "3.8" and sys_platform == "win32" and (python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.6" and python_full_version >= "3.7.0") and (python_version >= "3.7" and python_full_version < "3.0.0" and platform_system == "Windows" or platform_system == "Windows" and python_version >= "3.7" and python_full_version >= "3.7.0") and (python_version >= "2.7" and python_full_version < "3.0.0" and platform_system == "Windows" or python_full_version >= "3.7.0" and platform_system == "Windows") and (python_version >= "3.7" and python_full_version < "3.0.0" and platform_system == "Windows" and (python_version >= "3.7" and python_full_version < "3.9.7" or python_full_version > "3.9.7" and python_version >= "3.7") or platform_system == "Windows" and python_version >= "3.7" and (python_version >= "3.7" and python_full_version < "3.9.7" or python_full_version > "3.9.7" and python_version >= "3.7") and python_full_version >= "3.7.0") and python_full_version < "4.0.0"
|
||||
colorama==0.4.6; python_full_version >= "3.7.2" and python_version >= "3.8" and sys_platform == "win32" and (python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.6" and python_full_version >= "3.7.0") and (python_version >= "3.7" and python_full_version < "3.0.0" and platform_system == "Windows" or platform_system == "Windows" and python_version >= "3.7" and python_full_version >= "3.7.0") and (python_version >= "3.7" and python_full_version < "3.0.0" and platform_system == "Windows" and (python_version >= "3.7" and python_full_version < "3.9.7" or python_full_version > "3.9.7" and python_version >= "3.7") or platform_system == "Windows" and python_version >= "3.7" and (python_version >= "3.7" and python_full_version < "3.9.7" or python_full_version > "3.9.7" and python_version >= "3.7") and python_full_version >= "3.7.0") and python_full_version < "4.0.0"
|
||||
commonmark==0.9.1; python_full_version >= "3.6.3" and python_full_version < "4.0.0" and (python_version >= "3.7" and python_full_version < "3.9.7" or python_full_version > "3.9.7")
|
||||
convertdate==2.4.0; python_version >= "3.7" and python_version < "4"
|
||||
coverage==6.5.0; python_version >= "3.7"
|
||||
@@ -164,6 +164,7 @@ pandas-market-calendars==3.2; python_full_version >= "3.7.0"
|
||||
pandas-ta==0.3.14b
|
||||
pandas==1.5.1; python_version >= "3.8"
|
||||
pandocfilters==1.5.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7"
|
||||
papermill @ git+https://github.com/nteract/papermill.git@main ; python_version >= "3.7"
|
||||
parso==0.8.3; python_version >= "3.8" and python_full_version >= "3.7.0" and python_full_version < "4.0.0"
|
||||
pathspec==0.10.1; python_version >= "3.7"
|
||||
patsy==0.5.3; python_version >= "3.8"
|
||||
@@ -277,7 +278,7 @@ stocksera==0.1.21; python_version >= "3.6"
|
||||
streamlit==1.13.0; (python_version >= "3.7" and python_full_version < "3.9.7") or (python_full_version > "3.9.7")
|
||||
tabulate==0.9.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7"
|
||||
temporal-cache==0.1.4
|
||||
tenacity==8.1.0; python_version >= "3.6"
|
||||
tenacity==8.1.0; python_version >= "3.7"
|
||||
terminado==0.16.0; python_version >= "3.7"
|
||||
thepassiveinvestor==1.0.10
|
||||
threadpoolctl==3.1.0; python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.8"
|
||||
@@ -290,7 +291,7 @@ tomlkit==0.11.5; python_version >= "3.6" and python_version < "4.0" and python_f
|
||||
toolz==0.12.0; python_version >= "3.8" and python_version < "4.0" and python_full_version >= "3.7.0" and (python_version >= "3.7" and python_full_version < "3.9.7" or python_full_version > "3.9.7" and python_version >= "3.7")
|
||||
torch==1.11.0; python_full_version >= "3.7.0"
|
||||
tornado==6.2; python_version >= "3.7" and python_full_version < "3.9.7" and python_full_version >= "3.7.0" or python_full_version > "3.9.7" and python_version >= "3.7" and python_full_version < "4.0.0"
|
||||
tqdm==4.64.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0"
|
||||
tqdm==4.64.1; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7"
|
||||
tradingview-ta==3.3.0; python_version >= "3.6"
|
||||
traitlets==5.5.0; python_full_version >= "3.7.0" and python_full_version < "4.0.0" and python_version >= "3.8"
|
||||
types-python-dateutil==2.8.19.2
|
||||
|
||||
5
requirements.txt
vendored
5
requirements.txt
vendored
@@ -140,6 +140,7 @@ pandas-market-calendars==3.2; python_full_version >= "3.7.0"
|
||||
pandas-ta==0.3.14b
|
||||
pandas==1.5.1; python_version >= "3.8"
|
||||
pandocfilters==1.5.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7"
|
||||
papermill @ git+https://github.com/nteract/papermill.git@main ; python_version >= "3.7"
|
||||
parso==0.8.3; python_version >= "3.8"
|
||||
patsy==0.5.3; python_version >= "3.8"
|
||||
pexpect==4.8.0; sys_platform != "win32" and python_version >= "3.8"
|
||||
@@ -232,7 +233,7 @@ stocksera==0.1.21; python_version >= "3.6"
|
||||
streamlit==1.13.0; (python_version >= "3.7" and python_full_version < "3.9.7") or (python_full_version > "3.9.7")
|
||||
tabulate==0.9.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7"
|
||||
temporal-cache==0.1.4
|
||||
tenacity==8.1.0; python_version >= "3.6"
|
||||
tenacity==8.1.0; python_version >= "3.7"
|
||||
terminado==0.16.0; python_version >= "3.7"
|
||||
thepassiveinvestor==1.0.10
|
||||
threadpoolctl==3.1.0; python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.8"
|
||||
@@ -242,7 +243,7 @@ toml==0.10.2; python_version >= "3.7" and python_full_version < "3.0.0" or pytho
|
||||
tomli==2.0.1; python_version >= "3.8"
|
||||
toolz==0.12.0; python_version >= "3.8" and python_version < "4.0" and python_full_version >= "3.7.0" and (python_version >= "3.7" and python_full_version < "3.9.7" or python_full_version > "3.9.7" and python_version >= "3.7")
|
||||
tornado==6.2; python_version >= "3.7" and python_full_version < "3.9.7" or python_full_version > "3.9.7" and python_version >= "3.7"
|
||||
tqdm==4.64.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0"
|
||||
tqdm==4.64.1; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7"
|
||||
tradingview-ta==3.3.0; python_version >= "3.6"
|
||||
traitlets==5.5.0; python_full_version >= "3.7.0" and python_version >= "3.8"
|
||||
typing-extensions==4.4.0; python_full_version >= "3.7.0" and python_full_version < "4.0.0" and python_version < "3.9" and python_version >= "3.7" and (python_version >= "3.7" and python_full_version < "3.9.7" or python_full_version > "3.9.7" and python_version >= "3.7") and (python_version >= "3.7" and python_full_version < "3.9.7" or python_full_version > "3.9.7")
|
||||
|
||||
0
tests/openbb_terminal/reports/__init__.py
Normal file
0
tests/openbb_terminal/reports/__init__.py
Normal file
358
tests/openbb_terminal/reports/test_reports_controller.py
Normal file
358
tests/openbb_terminal/reports/test_reports_controller.py
Normal file
@@ -0,0 +1,358 @@
|
||||
# IMPORTATION STANDARD
|
||||
import os
|
||||
|
||||
# IMPORTATION THIRDPARTY
|
||||
import pytest
|
||||
|
||||
# IMPORTATION INTERNAL
|
||||
from openbb_terminal.reports import reports_controller, reports_model
|
||||
|
||||
|
||||
# pylint: disable=E1101
|
||||
# pylint: disable=W0603
|
||||
# pylint: disable=E1111
|
||||
|
||||
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
@pytest.mark.parametrize(
|
||||
"queue, expected",
|
||||
[
|
||||
(["load", "help"], ["help"]),
|
||||
(["quit", "help"], ["help"]),
|
||||
],
|
||||
)
|
||||
def test_menu_with_queue(expected, mocker, queue):
|
||||
path_controller = "openbb_terminal.reports.reports_controller"
|
||||
|
||||
# MOCK SWITCH
|
||||
mocker.patch(
|
||||
target=f"{path_controller}.ReportController.switch",
|
||||
return_value=["quit"],
|
||||
)
|
||||
result_menu = reports_controller.ReportController(queue=queue).menu()
|
||||
|
||||
assert result_menu == expected
|
||||
|
||||
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
def test_menu_without_queue_completion(mocker):
|
||||
path_controller = "openbb_terminal.reports.reports_controller"
|
||||
|
||||
# ENABLE AUTO-COMPLETION : HELPER_FUNCS.MENU
|
||||
mocker.patch(
|
||||
target="openbb_terminal.feature_flags.USE_PROMPT_TOOLKIT",
|
||||
new=True,
|
||||
)
|
||||
mocker.patch(
|
||||
target="openbb_terminal.parent_classes.session",
|
||||
)
|
||||
mocker.patch(
|
||||
target="openbb_terminal.parent_classes.session.prompt",
|
||||
return_value="quit",
|
||||
)
|
||||
|
||||
# DISABLE AUTO-COMPLETION : CONTROLLER.COMPLETER
|
||||
mocker.patch.object(
|
||||
target=reports_controller.obbff,
|
||||
attribute="USE_PROMPT_TOOLKIT",
|
||||
new=True,
|
||||
)
|
||||
mocker.patch(
|
||||
target=f"{path_controller}.session",
|
||||
)
|
||||
mocker.patch(
|
||||
target=f"{path_controller}.session.prompt",
|
||||
return_value="quit",
|
||||
)
|
||||
|
||||
result_menu = reports_controller.ReportController(queue=None).menu()
|
||||
|
||||
assert result_menu == ["help"]
|
||||
|
||||
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
@pytest.mark.parametrize(
|
||||
"mock_input",
|
||||
["help", "homee help", "home help", "mock"],
|
||||
)
|
||||
def test_menu_without_queue_sys_exit(mock_input, mocker):
|
||||
path_controller = "openbb_terminal.reports.reports_controller"
|
||||
|
||||
# DISABLE AUTO-COMPLETION
|
||||
mocker.patch.object(
|
||||
target=reports_controller.obbff,
|
||||
attribute="USE_PROMPT_TOOLKIT",
|
||||
new=False,
|
||||
)
|
||||
mocker.patch(
|
||||
target=f"{path_controller}.session",
|
||||
return_value=None,
|
||||
)
|
||||
|
||||
# MOCK USER INPUT
|
||||
mocker.patch("builtins.input", return_value=mock_input)
|
||||
|
||||
# MOCK SWITCH
|
||||
class SystemExitSideEffect:
|
||||
def __init__(self):
|
||||
self.first_call = True
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
if self.first_call:
|
||||
self.first_call = False
|
||||
raise SystemExit()
|
||||
return ["quit"]
|
||||
|
||||
mock_switch = mocker.Mock(side_effect=SystemExitSideEffect())
|
||||
mocker.patch(
|
||||
target=f"{path_controller}.ReportController.switch",
|
||||
new=mock_switch,
|
||||
)
|
||||
|
||||
result_menu = reports_controller.ReportController(queue=None).menu()
|
||||
|
||||
assert result_menu == ["help"]
|
||||
|
||||
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
@pytest.mark.record_stdout
|
||||
def test_print_help():
|
||||
controller = reports_controller.ReportController(queue=None)
|
||||
controller.print_help()
|
||||
|
||||
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
@pytest.mark.parametrize(
|
||||
"an_input, expected_queue",
|
||||
[
|
||||
("", []),
|
||||
("/help", ["home", "help"]),
|
||||
("help/help", ["help", "help"]),
|
||||
("q", ["quit"]),
|
||||
("h", []),
|
||||
(
|
||||
"r",
|
||||
[
|
||||
"quit",
|
||||
"reset",
|
||||
"reports",
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_switch(an_input, expected_queue):
|
||||
controller = reports_controller.ReportController(queue=None)
|
||||
queue = controller.switch(an_input=an_input)
|
||||
|
||||
assert queue == expected_queue
|
||||
|
||||
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
def test_call_cls(mocker):
|
||||
mocker.patch("os.system")
|
||||
|
||||
controller = reports_controller.ReportController(queue=None)
|
||||
controller.call_cls([])
|
||||
|
||||
assert controller.queue == []
|
||||
os.system.assert_called_once_with("cls||clear")
|
||||
|
||||
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
@pytest.mark.parametrize(
|
||||
"func, queue, expected_queue",
|
||||
[
|
||||
(
|
||||
"call_exit",
|
||||
[],
|
||||
["quit", "quit"],
|
||||
),
|
||||
("call_exit", ["help"], ["quit", "quit", "help"]),
|
||||
("call_home", [], ["quit"]),
|
||||
("call_help", [], []),
|
||||
("call_quit", [], ["quit"]),
|
||||
("call_quit", ["help"], ["quit", "help"]),
|
||||
(
|
||||
"call_reset",
|
||||
[],
|
||||
[
|
||||
"quit",
|
||||
"reset",
|
||||
"reports",
|
||||
],
|
||||
),
|
||||
(
|
||||
"call_reset",
|
||||
["help"],
|
||||
[
|
||||
"quit",
|
||||
"reset",
|
||||
"reports",
|
||||
"help",
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_call_func_expect_queue(expected_queue, func, queue):
|
||||
controller = reports_controller.ReportController(queue=queue)
|
||||
result = getattr(controller, func)([])
|
||||
|
||||
assert result is None
|
||||
assert controller.queue == expected_queue
|
||||
|
||||
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
@pytest.mark.parametrize(
|
||||
"tested_func, other_args, mocked_func, called_args, called_kwargs",
|
||||
[
|
||||
(
|
||||
"call_etf",
|
||||
[
|
||||
"--symbol=SPY",
|
||||
],
|
||||
"reports_model.render_report",
|
||||
[],
|
||||
dict(
|
||||
input_path=str(reports_model.REPORTS_FOLDER / "etf"),
|
||||
args_dict={"symbol": "SPY"},
|
||||
),
|
||||
),
|
||||
(
|
||||
"call_forex",
|
||||
[
|
||||
"--symbol=EURUSD",
|
||||
],
|
||||
"reports_model.render_report",
|
||||
[],
|
||||
dict(
|
||||
input_path=str(reports_model.REPORTS_FOLDER / "forex"),
|
||||
args_dict={"symbol": "EURUSD"},
|
||||
),
|
||||
),
|
||||
(
|
||||
"call_portfolio",
|
||||
[
|
||||
"--orderbook=market.csv",
|
||||
],
|
||||
"reports_model.render_report",
|
||||
[],
|
||||
dict(
|
||||
input_path=str(reports_model.REPORTS_FOLDER / "portfolio"),
|
||||
args_dict={"orderbook": "market.csv"},
|
||||
),
|
||||
),
|
||||
(
|
||||
"call_economy",
|
||||
[],
|
||||
"reports_model.render_report",
|
||||
[],
|
||||
dict(
|
||||
input_path=str(reports_model.REPORTS_FOLDER / "economy"),
|
||||
args_dict={},
|
||||
),
|
||||
),
|
||||
(
|
||||
"call_equity",
|
||||
[
|
||||
"--symbol=TSLA",
|
||||
],
|
||||
"reports_model.render_report",
|
||||
[],
|
||||
dict(
|
||||
input_path=str(reports_model.REPORTS_FOLDER / "equity"),
|
||||
args_dict={"symbol": "TSLA"},
|
||||
),
|
||||
),
|
||||
(
|
||||
"call_crypto",
|
||||
[
|
||||
"--symbol=BTC",
|
||||
],
|
||||
"reports_model.render_report",
|
||||
[],
|
||||
dict(
|
||||
input_path=str(reports_model.REPORTS_FOLDER / "crypto"),
|
||||
args_dict={"symbol": "BTC"},
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_call_func(
|
||||
tested_func, mocked_func, other_args, called_args, called_kwargs, mocker
|
||||
):
|
||||
path_controller = "openbb_terminal.reports.reports_controller"
|
||||
|
||||
# MOCK REMOVE
|
||||
mocker.patch(target=f"{path_controller}.os.remove")
|
||||
|
||||
if mocked_func:
|
||||
mock = mocker.Mock()
|
||||
mocker.patch(
|
||||
target=f"{path_controller}.{mocked_func}",
|
||||
new=mock,
|
||||
)
|
||||
|
||||
controller = reports_controller.ReportController(queue=None)
|
||||
getattr(controller, tested_func)(other_args)
|
||||
|
||||
if called_args or called_kwargs:
|
||||
mock.assert_called_once_with(*called_args, **called_kwargs)
|
||||
else:
|
||||
mock.assert_called_once()
|
||||
else:
|
||||
controller = reports_controller.ReportController(queue=None)
|
||||
getattr(controller, tested_func)(other_args)
|
||||
|
||||
|
||||
# TODO: No test for forecast command, because it depends on darts being available
|
||||
@pytest.mark.vcr(record_mode="none")
|
||||
@pytest.mark.parametrize(
|
||||
"tested_func, other_args, mocked_func, called_args, called_kwargs",
|
||||
[
|
||||
(
|
||||
"call_forecast",
|
||||
[
|
||||
"--symbol=TSLA",
|
||||
],
|
||||
"reports_model.render_report",
|
||||
[],
|
||||
dict(
|
||||
input_path=str(reports_model.REPORTS_FOLDER / "forecast"),
|
||||
args_dict={"symbol": "TSLA"},
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_call_func_forecast(
|
||||
tested_func, mocked_func, other_args, called_args, called_kwargs, mocker
|
||||
):
|
||||
path_controller = "openbb_terminal.reports.reports_controller"
|
||||
|
||||
# MOCK REMOVE
|
||||
mocker.patch(target=f"{path_controller}.os.remove")
|
||||
|
||||
try:
|
||||
import darts # pyright: reportMissingImports=false # noqa: F401, E501 #pylint: disable=import-outside-toplevel, unused-import
|
||||
|
||||
forecast = True
|
||||
except ImportError:
|
||||
forecast = False
|
||||
|
||||
if forecast:
|
||||
if mocked_func:
|
||||
mock = mocker.Mock()
|
||||
mocker.patch(
|
||||
target=f"{path_controller}.{mocked_func}",
|
||||
new=mock,
|
||||
)
|
||||
|
||||
controller = reports_controller.ReportController(queue=None)
|
||||
getattr(controller, tested_func)(other_args)
|
||||
|
||||
if called_args or called_kwargs:
|
||||
mock.assert_called_once_with(*called_args, **called_kwargs)
|
||||
else:
|
||||
mock.assert_called_once()
|
||||
else:
|
||||
controller = reports_controller.ReportController(queue=None)
|
||||
getattr(controller, tested_func)(other_args)
|
||||
14
tests/openbb_terminal/reports/txt/test_reports_controller/test_print_help.txt
vendored
Normal file
14
tests/openbb_terminal/reports/txt/test_reports_controller/test_print_help.txt
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
Build and run your custom reports or try one of our templates:
|
||||
|
||||
OpenBB reports:
|
||||
crypto <symbol>
|
||||
economy
|
||||
equity <symbol>
|
||||
etf <symbol>
|
||||
forecast <symbol>
|
||||
forex <symbol>
|
||||
portfolio <orderbook>
|
||||
|
||||
Custom reports:
|
||||
run Run a Jupyter notebook from OpenBBUserData/reports/custom reports
|
||||
|
||||
@@ -1 +1,6 @@
|
||||
|
||||
---
|
||||
geekdocCollapseSection: true
|
||||
---
|
||||
|
||||
**Available commands:**
|
||||
{{< toc-tree >}}
|
||||
13
website/content/terminal/jupyter/reports/crypto/_index.md
vendored
Normal file
13
website/content/terminal/jupyter/reports/crypto/_index.md
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
```
|
||||
usage: chains [-j] [-h]
|
||||
```
|
||||
|
||||
Opens a voila dashboard showing option chain information for a given ticker and expiration
|
||||
|
||||
```
|
||||
optional arguments:
|
||||
-j, --jupyter opens in jupyterlab instead of voila (default: False)
|
||||
-d, --dark runs voila in dark mode (default: False)
|
||||
-n, --no-input does not ask the user if it can open a server (default: False)
|
||||
-h, --help show this help message
|
||||
```
|
||||
8
website/content/terminal/jupyter/reports/economy/_index.md
vendored
Normal file
8
website/content/terminal/jupyter/reports/economy/_index.md
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
```
|
||||
usage: economy [-h]
|
||||
|
||||
Run economy report.
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message (default: False)
|
||||
```
|
||||
9
website/content/terminal/jupyter/reports/equity/_index.md
vendored
Normal file
9
website/content/terminal/jupyter/reports/equity/_index.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
```
|
||||
usage: equity [--symbol SYMBOL] [-h]
|
||||
|
||||
Run equity report.
|
||||
|
||||
optional arguments:
|
||||
--symbol SYMBOL symbol (default: msft)
|
||||
-h, --help show this help message (default: False)
|
||||
```
|
||||
9
website/content/terminal/jupyter/reports/etf/_index.md
vendored
Normal file
9
website/content/terminal/jupyter/reports/etf/_index.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
```
|
||||
usage: etf [--symbol SYMBOL] [-h]
|
||||
|
||||
Run etf report.
|
||||
|
||||
optional arguments:
|
||||
--symbol SYMBOL symbol (default: VTI)
|
||||
-h, --help show this help message (default: False)
|
||||
```
|
||||
9
website/content/terminal/jupyter/reports/forecast/_index.md
vendored
Normal file
9
website/content/terminal/jupyter/reports/forecast/_index.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
```
|
||||
usage: forecast [--symbol SYMBOL] [-h]
|
||||
|
||||
Run forecast report.
|
||||
|
||||
optional arguments:
|
||||
--symbol SYMBOL symbol (default: VTI)
|
||||
-h, --help show this help message (default: False)
|
||||
```
|
||||
12
website/content/terminal/jupyter/reports/forex/_index.md
vendored
Normal file
12
website/content/terminal/jupyter/reports/forex/_index.md
vendored
Normal file
File diff suppressed because one or more lines are too long
13
website/content/terminal/jupyter/reports/portfolio/_index.md
vendored
Normal file
13
website/content/terminal/jupyter/reports/portfolio/_index.md
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
```
|
||||
usage: portfolio
|
||||
[--orderbook {Public_Equity_Orderbook.xlsx,Public_Equity_Orderbook_No_Categorization.xlsx,market.csv,example.csv,bitcoin.csv}]
|
||||
[-h]
|
||||
|
||||
Run portfolio report.
|
||||
|
||||
optional arguments:
|
||||
--orderbook {Public_Equity_Orderbook.xlsx,Public_Equity_Orderbook_No_Categorization.xlsx,market.csv,example.csv,bitcoin.csv}
|
||||
orderbook (default:
|
||||
Public_Equity_Orderbook.xlsx)
|
||||
-h, --help show this help message (default: False)
|
||||
```
|
||||
16
website/content/terminal/jupyter/reports/run/_index.md
vendored
Normal file
16
website/content/terminal/jupyter/reports/run/_index.md
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
```
|
||||
usage: run [-f {test.ipynb}] [-p PARAMETERS [PARAMETERS ...]] [-h]
|
||||
|
||||
Run a notebook from this folder:
|
||||
'/Users/diogosousa/OpenBBUserData/reports'.
|
||||
|
||||
optional arguments:
|
||||
-f {test.ipynb}, --file {test.ipynb}
|
||||
The file to be loaded (default: None)
|
||||
-p PARAMETERS [PARAMETERS ...], --parameters PARAMETERS [PARAMETERS ...]
|
||||
Report parameters with format 'name:value'.
|
||||
(default: None)
|
||||
-h, --help show this help message (default: False)
|
||||
|
||||
For more information and examples, use 'about run' to access the related guide.
|
||||
```
|
||||
17
website/data/menu/main.yml
vendored
17
website/data/menu/main.yml
vendored
@@ -883,6 +883,23 @@ main:
|
||||
ref: /terminal/jupyter/dashboards/vsurf
|
||||
- name: reports
|
||||
ref: /terminal/jupyter/reports
|
||||
sub:
|
||||
- name: crypto
|
||||
ref: /terminal/jupyter/reports/crypto
|
||||
- name: economy
|
||||
ref: /terminal/jupyter/reports/economy
|
||||
- name: equity
|
||||
ref: /terminal/jupyter/reports/equity
|
||||
- name: etf
|
||||
ref: /terminal/jupyter/reports/etf
|
||||
- name: forecast
|
||||
ref: /terminal/jupyter/reports/forecast
|
||||
- name: forex
|
||||
ref: /terminal/jupyter/reports/forex
|
||||
- name: portfolio
|
||||
ref: /terminal/jupyter/reports/portfolio
|
||||
- name: run
|
||||
ref: /terminal/jupyter/reports/run
|
||||
- name: portfolio
|
||||
ref: /terminal/portfolio
|
||||
sub:
|
||||
|
||||
Reference in New Issue
Block a user