Merge branch 'develop' into feature/openbb-sdk-v4

This commit is contained in:
Henrique Joaquim
2023-08-17 09:40:02 +01:00
committed by GitHub
66 changed files with 606 additions and 433 deletions

4
README.md vendored
View File

@@ -135,7 +135,9 @@ Any of our social media platforms: [openbb.co/links](https://openbb.co/links)
## 6. Star History
This is a proxy of our growth and that we are just getting started. But for more metrics important to us check [openbb.co/open](https://openbb.co/open).
This is a proxy of our growth and that we are just getting started.
But for more metrics important to us check [openbb.co/open](https://openbb.co/open).
[![Star History Chart](https://api.star-history.com/svg?repos=openbb-finance/OpenBBTerminal&type=Date)](https://star-history.com/#openbb-finance/OpenBBTerminal&Date)

View File

@@ -1,2 +1,2 @@
OPENBBTERMINAL_DOCKER_REGISTRY="ghcr.io"
OPENBBTERMINAL_DOCKER_RELEASE_VERSION="3.2.0"
OPENBBTERMINAL_DOCKER_RELEASE_VERSION="3.2.1"

View File

@@ -10,7 +10,7 @@
!define NAME "OpenBB Terminal"
!define COMPANY "OpenBB"
!define APPFILE "OpenBBTerminal.exe"
!define VERSION "3.2.0"
!define VERSION "3.2.1"
!define SLUG "${NAME} v${VERSION}"
;--------------------------------
@@ -20,8 +20,8 @@
VIAddVersionKey Comments "An installer for OpenBB Terminal. For additional details, visit OpenBB.co"
VIAddVersionKey CompanyName OpenBB.co
VIAddVersionKey FileDescription "OpenBB Terminal Program"
VIAddVersionKey FileVersion 3.2.0.0
VIAddVersionKey ProductVersion 3.2.0.0
VIAddVersionKey FileVersion 3.2.1.0
VIAddVersionKey ProductVersion 3.2.1.0
VIAddVersionKey InternalName "OpenBB Terminal"
;--------------------------------

View File

@@ -32,10 +32,10 @@ VSVersionInfo(
[StringStruct('Comments', 'The OpenBB Terminal. For additional details, visit OpenBB.co'),
StringStruct('CompanyName', 'OpenBB'),
StringStruct('FileDescription', 'OpenBB Terminal Program'),
StringStruct('FileVersion', '3.2.0.0'),
StringStruct('FileVersion', '3.2.1.0'),
StringStruct('InternalName', 'OpenBB Terminal'),
StringStruct('ProductName', 'OpenBB Terminal'),
StringStruct('ProductVersion', '3.2.0.0')])
StringStruct('ProductVersion', '3.2.1.0')])
]),
VarFileInfo([VarStruct('Translation', [1033, 1200])])
]

View File

@@ -255,6 +255,7 @@ class AccountController(BaseController):
auth_header=get_current_user().profile.get_auth_header(),
page=ns_parser.page,
size=ns_parser.size,
base_url=Hub.BackendEnvironment.BASE_URL,
)
df, page, pages = get_personal_routines_info(response)
if not df.empty:
@@ -356,6 +357,7 @@ class AccountController(BaseController):
"routine": routine,
"tags": tags,
"public": ns_parser.public,
"base_url": Hub.BackendEnvironment.BASE_URL,
}
response = Hub.upload_routine(**kwargs) # type: ignore
@@ -412,6 +414,7 @@ class AccountController(BaseController):
response = Hub.download_routine(
auth_header=get_current_user().profile.get_auth_header(),
uuid=self.REMOTE_CHOICES[name],
base_url=Hub.BackendEnvironment.BASE_URL,
)
if response and response.status_code == 200:
data = [response.json(), "personal"]
@@ -499,6 +502,7 @@ class AccountController(BaseController):
response = Hub.delete_routine(
auth_header=get_current_user().profile.get_auth_header(),
uuid=self.REMOTE_CHOICES[name],
base_url=Hub.BackendEnvironment.BASE_URL,
)
if (
response
@@ -551,6 +555,7 @@ class AccountController(BaseController):
response = Hub.generate_personal_access_token(
auth_header=get_current_user().profile.get_auth_header(),
base_url=Hub.BackendEnvironment.BASE_URL,
days=ns_parser.days,
)
if response and response.status_code == 200:

View File

@@ -1,6 +1,7 @@
"""Reddit View."""
__docformat__ = "numpy"
import io
import logging
import os
import textwrap
@@ -212,10 +213,10 @@ def display_wsb_community(limit: int = 10, new: bool = False):
Flag to sort by new instead of hot, by default False
"""
subs = reddit_model.get_wsb_community(limit, new)
# I am not proud of this, but it works to eliminate the max recursion bug
subs = pd.read_csv(io.StringIO(subs.to_csv()), index_col=0).fillna("-")
if not subs.empty:
# for sub in subs.iterrows():
# print_reddit_post(sub)
print(print_rich_table(subs))
print_rich_table(subs)
@log_start_end(log=logger)

View File

@@ -18,7 +18,7 @@ from openbb_terminal.rich_config import console
logger = logging.getLogger(__name__)
BASE_URL = "https://api.ultimainsights.ai/v1"
ULTIMA_BASE_URL = "https://api.ultimainsights.ai/v1"
NO_API_KEY = "REPLACE_ME"
@@ -69,10 +69,10 @@ def get_news(term: str = "", sort: str = "articlePublishedDate") -> pd.DataFrame
if term in supported_terms():
if auth_header:
data = request(
f"{BASE_URL}/getNewsArticles/{term}", headers=auth_header
f"{ULTIMA_BASE_URL}/getNewsArticles/{term}", headers=auth_header
)
else:
data = request(f"{BASE_URL}/getNewsArticles/{term}")
data = request(f"{ULTIMA_BASE_URL}/getNewsArticles/{term}")
else:
console.print(
"[red]Ticker not supported. Unable to retrieve data\n[/red]"
@@ -150,7 +150,7 @@ def supported_terms() -> list:
# https://stackoverflow.com/questions/27835619/urllib-and-ssl-certificate-verify-failed-error/73270162#73270162
os.environ["REQUESTS_CA_BUNDLE"] = certifi.where()
os.environ["SSL_CERT_FILE"] = certifi.where()
data = request(f"{BASE_URL}/supportedTickers")
data = request(f"{ULTIMA_BASE_URL}/supportedTickers")
return list(data.json())
@@ -176,7 +176,7 @@ def get_company_info(ticker: str) -> dict:
os.environ["SSL_CERT_FILE"] = certifi.where()
if ticker in supported_terms():
data = request(f"{BASE_URL}/getCompanyInfo/{ticker}")
data = request(f"{ULTIMA_BASE_URL}/getCompanyInfo/{ticker}")
return data.json()
console.print("[red]Ticker not supported. Unable to retrieve data\n[/red]")
return {}
@@ -213,9 +213,11 @@ def get_top_headlines(ticker: str) -> dict:
if ticker in supported_terms():
if auth_header:
data = request(f"{BASE_URL}/getTopHeadlines/{ticker}", headers=auth_header)
data = request(
f"{ULTIMA_BASE_URL}/getTopHeadlines/{ticker}", headers=auth_header
)
else:
data = request(f"{BASE_URL}/getTopHeadlines/{ticker}")
data = request(f"{ULTIMA_BASE_URL}/getTopHeadlines/{ticker}")
if (
hasattr(data, "status") and data.status_code == 429
): # If data request failed

View File

@@ -1,11 +1,11 @@
from argparse import ArgumentParser
from contextlib import contextmanager
from inspect import isfunction, unwrap
from os import environ
from types import MethodType
from typing import Callable, List
from unittest.mock import patch
from openbb_terminal.core.session.current_system import get_current_system
from openbb_terminal.helper_funcs import check_file_type_saved, check_positive
from openbb_terminal.rich_config import get_ordered_list_sources
@@ -211,7 +211,7 @@ def __patch_controller_functions(controller):
),
]
if str(environ.get("DEBUG_MODE", "false")).lower() != "true":
if not get_current_system().DEBUG_MODE:
rich.start()
patched_function_list = []
for patcher in patcher_list:
@@ -219,7 +219,7 @@ def __patch_controller_functions(controller):
yield patched_function_list
if str(environ.get("DEBUG_MODE", "false")).lower() != "true":
if not get_current_system().DEBUG_MODE:
rich.stop()
for patcher in patcher_list:
patcher.stop()
@@ -317,7 +317,7 @@ def build_controller_choice_map(controller) -> dict:
argument_parser=argument_parser
)
except Exception as exception:
if str(environ.get("DEBUG_MODE", "false")).lower() == "true":
if get_current_system().DEBUG_MODE:
raise Exception(
f"On command : `{command}`.\n{str(exception)}"
) from exception

View File

@@ -28,7 +28,7 @@ class SystemModel(BaseModel):
PLATFORM: str = str(platform.platform())
# OpenBB section
VERSION: str = "3.2.0"
VERSION: str = "3.2.1"
# Logging section
LOGGING_APP_ID: str = "REPLACE_ME"
@@ -54,6 +54,7 @@ class SystemModel(BaseModel):
# Others
TEST_MODE: bool = False
DEBUG_MODE: bool = False
DEV_BACKEND: bool = False
ENABLE_AUTHENTICATION: bool = True
HEADLESS: bool = False

View File

@@ -17,7 +17,9 @@ import plotly.graph_objects as go
from packaging import version
from reportlab.graphics import renderPDF
# pylint: disable=C0415
from openbb_terminal.core.session.constants import BackendEnvironment
# pylint: disable=C0411,C0412,C0415
try:
from pywry import PyWry
except ImportError as e:
@@ -441,8 +443,10 @@ class Backend(PyWry):
self.check_backend()
endpoint = {True: "login", False: "logout"}[login]
json_url = f"{BackendEnvironment.HUB_URL}{endpoint}?pywry=true"
outgoing = dict(
json_data=dict(url=f"https://my.openbb.co/{endpoint}?pywry=true"),
json_data=dict(url=json_url),
**self.get_kwargs(endpoint.title()),
width=900,
height=800,

View File

@@ -59,7 +59,6 @@ class StocksController(model.StocksRoot):
`sentiment_chart`: Plots sentiments from symbol\n
`snews`: Get headlines sentiment using VADER model over time. [Source: Finnhub]\n
`snews_chart`: Display stock price and headlines sentiment using VADER model over time. [Source: Finnhub]\n
`spacc`: Get top tickers from r/SPACs [Source: reddit].\n
`stalker`: Gets messages from given user [Source: stocktwits].\n
`text_sent`: Find the sentiment of a post and related comments.\n
`trending`: Get trending tickers from stocktwits [Source: stocktwits].\n

View File

@@ -59,7 +59,6 @@ class StocksBehavioralAnalysis(Category):
`sentiment_chart`: Plots sentiments from symbol\n
`snews`: Get headlines sentiment using VADER model over time. [Source: Finnhub]\n
`snews_chart`: Display stock price and headlines sentiment using VADER model over time. [Source: Finnhub]\n
`spacc`: Get top tickers from r/SPACs [Source: reddit].\n
`stalker`: Gets messages from given user [Source: stocktwits].\n
`text_sent`: Find the sentiment of a post and related comments.\n
`trending`: Get trending tickers from stocktwits [Source: stocktwits].\n
@@ -94,7 +93,6 @@ class StocksBehavioralAnalysis(Category):
self.snews_chart = (
lib.stocks_ba_finnhub_view.display_stock_price_headlines_sentiment
)
self.spacc = lib.stocks_ba_reddit_model.get_spac_community
self.stalker = lib.stocks_ba_stocktwits_model.get_stalker
self.text_sent = lib.stocks_ba_reddit_model.get_sentiment
self.trending = lib.stocks_ba_stocktwits_model.get_trending

View File

@@ -400,7 +400,6 @@ stocks.ba.regions,stocks_ba_google_model.get_regions,stocks_ba_google_view.displ
stocks.ba.rise,stocks_ba_google_model.get_rise,
stocks.ba.sentiment,stocks_ba_twitter_model.get_sentiment,stocks_ba_twitter_view.display_sentiment
stocks.ba.snews,stocks_ba_finnhub_model.get_headlines_sentiment,stocks_ba_finnhub_view.display_stock_price_headlines_sentiment
stocks.ba.spacc,stocks_ba_reddit_model.get_spac_community,
stocks.ba.stalker,stocks_ba_stocktwits_model.get_stalker,
stocks.ba.text_sent,stocks_ba_reddit_model.get_sentiment,
stocks.ba.trending,stocks_ba_stocktwits_model.get_trending,
1 trail model view
400 stocks.ba.rise stocks_ba_google_model.get_rise
401 stocks.ba.sentiment stocks_ba_twitter_model.get_sentiment stocks_ba_twitter_view.display_sentiment
402 stocks.ba.snews stocks_ba_finnhub_model.get_headlines_sentiment stocks_ba_finnhub_view.display_stock_price_headlines_sentiment
stocks.ba.spacc stocks_ba_reddit_model.get_spac_community
403 stocks.ba.stalker stocks_ba_stocktwits_model.get_stalker
404 stocks.ba.text_sent stocks_ba_reddit_model.get_sentiment
405 stocks.ba.trending stocks_ba_stocktwits_model.get_trending

View File

@@ -1,13 +1,16 @@
BASE_URL = "https://payments.openbb.co/"
# BASE_URL = "http://127.0.0.1:8000/"
class HubEnvironment:
# BASE_URL = "http://127.0.0.1:8000/"
BASE_URL = "https://payments.openbb.co/"
HUB_URL = "https://my.openbb.co/"
HUB_URL = "https://my.openbb.co/"
REGISTER_URL = HUB_URL + "register"
SUPPORT_URL = HUB_URL + "app/terminal/support"
SOURCES_URL = HUB_URL + "app/terminal/data-sources"
KEYS_URL = HUB_URL + "app/terminal/api-keys"
COLORS_URL = HUB_URL + "app/terminal/theme"
CHARTS_TABLES_URL = HUB_URL + "app/terminal/theme/charts-tables"
def __new__(cls, *args, **kwargs): # pylint: disable=W0613
"""Create a singleton instance of the backend."""
if not hasattr(cls, "instance"):
cls.instance = super().__new__(cls) # pylint: disable=E1120
return cls.instance
BackendEnvironment = HubEnvironment()
DEFAULT_ROUTINES_URL = "https://openbb-cms.directus.app/items/Routines"

View File

@@ -7,18 +7,22 @@ import requests
from jose import jwt
from openbb_terminal.core.session.constants import (
BASE_URL,
CONNECTION_ERROR_MSG,
CONNECTION_TIMEOUT_MSG,
DEFAULT_ROUTINES_URL,
TIMEOUT,
BackendEnvironment,
)
from openbb_terminal.core.session.current_system import get_current_system
from openbb_terminal.core.session.current_user import get_current_user
from openbb_terminal.rich_config import console
def create_session(
email: str, password: str, base_url: str = BASE_URL, timeout: int = TIMEOUT
email: str,
password: str,
base_url: str = BackendEnvironment.BASE_URL,
timeout: int = TIMEOUT,
) -> Optional[requests.Response]:
"""Create a session.
@@ -71,7 +75,7 @@ def check_token_expiration(token: str):
def create_session_from_token(
token: str, base_url: str = BASE_URL, timeout: int = TIMEOUT
token: str, base_url: str = BackendEnvironment.BASE_URL, timeout: int = TIMEOUT
):
"""Create a session from token.
@@ -108,7 +112,10 @@ def create_session_from_token(
def delete_session(
auth_header: str, token: str, base_url: str = BASE_URL, timeout: int = TIMEOUT
auth_header: str,
token: str,
base_url: str = BackendEnvironment.BASE_URL,
timeout: int = TIMEOUT,
) -> Optional[requests.Response]:
"""Delete the session.
@@ -190,7 +197,7 @@ def get_session(email: str, password: str) -> dict:
dict
The session info.
"""
response = create_session(email, password)
response = create_session(email, password, base_url=BackendEnvironment.BASE_URL)
if response is None:
return {}
return process_session_response(response)
@@ -209,14 +216,14 @@ def get_session_from_token(token: str) -> dict:
dict
The session info.
"""
response = create_session_from_token(token)
response = create_session_from_token(token, base_url=BackendEnvironment.BASE_URL)
if response is None:
return {}
return process_session_response(response)
def fetch_user_configs(
session: dict, base_url: str = BASE_URL, timeout: int = TIMEOUT
session: dict, base_url: str = BackendEnvironment.BASE_URL, timeout: int = TIMEOUT
) -> Optional[requests.Response]:
"""Fetch user configurations.
@@ -258,7 +265,10 @@ def fetch_user_configs(
def clear_user_configs(
config: str, auth_header: str, base_url: str = BASE_URL, timeout: int = TIMEOUT
config: str,
auth_header: str,
base_url: str = BackendEnvironment.BASE_URL,
timeout: int = TIMEOUT,
) -> Optional[requests.Response]:
"""Clear user configurations to the server.
@@ -307,7 +317,7 @@ def upload_user_field(
key: str,
value: Any,
auth_header: str,
base_url: str = BASE_URL,
base_url: str = BackendEnvironment.BASE_URL,
timeout: int = TIMEOUT,
silent: bool = False,
) -> Optional[requests.Response]:
@@ -365,7 +375,7 @@ def upload_config(
value: str,
type_: Literal["settings", "terminal_style"],
auth_header: str,
base_url: str = BASE_URL,
base_url: str = BackendEnvironment.BASE_URL,
timeout: int = TIMEOUT,
) -> Optional[requests.Response]:
"""Patch user configurations to the server.
@@ -429,7 +439,7 @@ def upload_routine(
override: bool = False,
tags: str = "",
public: bool = False,
base_url: str = BASE_URL,
base_url: str = BackendEnvironment.BASE_URL,
timeout: int = TIMEOUT,
) -> Optional[requests.Response]:
"""Send a routine to the server.
@@ -467,7 +477,6 @@ def upload_routine(
"version": get_current_system().VERSION,
"public": public,
}
try:
response = requests.post(
headers={"Authorization": auth_header},
@@ -476,7 +485,21 @@ def upload_routine(
timeout=timeout,
)
if response.status_code == 200:
console.print("[green]Successfully uploaded your routine.[/green]")
username = get_current_user().profile.username
console.print("[green]Successfully uploaded your routine.[/]")
run_env = BackendEnvironment.HUB_URL.rstrip("/")
if public:
console.print(
f"\n[yellow]Share or edit it at {run_env}/u/{username}/routine/{name.replace(' ', '-')}[/]"
)
else:
console.print(f"\n[yellow]Go to {run_env} to edit this script,[/]")
console.print(
f"[yellow]or even make it public so you can access it at "
f"{run_env}/u/{username}/routine/{name.replace(' ', '-')}[/]"
)
elif response.status_code != 409: # 409: routine already exists
console.print(
"[red]" + response.json().get("detail", "Unknown error.") + "[/red]"
@@ -496,7 +519,7 @@ def upload_routine(
def download_routine(
auth_header: str,
uuid: UUID,
base_url=BASE_URL,
base_url: str = BackendEnvironment.BASE_URL,
timeout: int = TIMEOUT,
) -> Optional[requests.Response]:
"""Download a routine from the server.
@@ -520,7 +543,7 @@ def download_routine(
try:
response = requests.get(
headers={"Authorization": auth_header},
url=base_url + "terminal/script/" + uuid,
url=base_url + "terminal/script/" + uuid, # type: ignore
timeout=timeout,
)
if response.status_code == 404:
@@ -542,7 +565,7 @@ def download_routine(
def delete_routine(
auth_header: str,
uuid: UUID,
base_url=BASE_URL,
base_url: str = BackendEnvironment.BASE_URL,
timeout: int = TIMEOUT,
) -> Optional[requests.Response]:
"""Delete a routine from the server.
@@ -566,7 +589,7 @@ def delete_routine(
try:
response = requests.delete(
headers={"Authorization": auth_header},
url=base_url + "terminal/script/" + uuid,
url=base_url + "terminal/script/" + uuid, # type: ignore
timeout=timeout,
)
if response.status_code == 200:
@@ -592,7 +615,7 @@ def list_routines(
fields: Optional[List[str]] = None,
page: int = 1,
size: int = 10,
base_url=BASE_URL,
base_url: str = BackendEnvironment.BASE_URL,
timeout: int = TIMEOUT,
silent: bool = False,
) -> Optional[requests.Response]:
@@ -683,7 +706,10 @@ def get_default_routines(
def generate_personal_access_token(
auth_header: str, base_url: str = BASE_URL, timeout: int = TIMEOUT, days: int = 30
auth_header: str,
base_url: str = BackendEnvironment.BASE_URL,
timeout: int = TIMEOUT,
days: int = 30,
) -> Optional[requests.Response]:
"""
Generate an OpenBB Personal Access Token.
@@ -732,7 +758,9 @@ def generate_personal_access_token(
def get_personal_access_token(
auth_header: str, base_url: str = BASE_URL, timeout: int = TIMEOUT
auth_header: str,
base_url: str = BackendEnvironment.BASE_URL,
timeout: int = TIMEOUT,
) -> Optional[requests.Response]:
"""
Show the user's OpenBB Personal Access Token.
@@ -777,7 +805,9 @@ def get_personal_access_token(
def revoke_personal_access_token(
auth_header: str, base_url: str = BASE_URL, timeout: int = TIMEOUT
auth_header: str,
base_url: str = BackendEnvironment.BASE_URL,
timeout: int = TIMEOUT,
) -> Optional[requests.Response]:
"""
Delete the user's OpenBB Personal Access Token.

View File

@@ -51,6 +51,7 @@ def download_routines(auth_header: str, silent: bool = False) -> list:
fields=["name", "script"],
page=1,
size=100,
base_url=Hub.BackendEnvironment.BASE_URL,
silent=silent,
)
if response and response.status_code == 200:

View File

@@ -5,11 +5,11 @@ from prompt_toolkit import PromptSession
import openbb_terminal.core.session.local_model as Local
from openbb_terminal.core.config.paths import PACKAGE_DIRECTORY
from openbb_terminal.core.plots.backend import plots_backend
from openbb_terminal.core.session.constants import (
REGISTER_URL,
SUPPORT_URL,
from openbb_terminal.core.session.constants import BackendEnvironment
from openbb_terminal.core.session.current_system import (
get_current_system,
set_system_variable,
)
from openbb_terminal.core.session.current_system import get_current_system
from openbb_terminal.core.session.session_model import (
LoginStatus,
create_session,
@@ -24,8 +24,12 @@ def display_welcome_message(links: bool = True) -> None:
with open(PACKAGE_DIRECTORY / "core" / "session" / "banner.txt") as f:
console.print(f"[menu]{f.read()}[/menu]\n")
if links:
console.print(f"Register : [cmds]{REGISTER_URL}[/cmds]")
console.print(f"Support : [cmds]{SUPPORT_URL}[/cmds]")
console.print(
f"Register : [cmds]{BackendEnvironment.HUB_URL + 'register'}[/cmds]"
)
console.print(
f"Support : [cmds]{BackendEnvironment.HUB_URL + 'app/terminal/support'}[/cmds]"
)
def get_user_input() -> Tuple[str, str, bool]:
@@ -135,13 +139,15 @@ def prompt_cli(welcome: bool = True):
# pylint: disable=inconsistent-return-statements
def launch_terminal(debug: bool = False, queue: Optional[List[str]] = None):
def launch_terminal(
debug: bool = False, dev: bool = False, queue: Optional[List[str]] = None
):
"""Launch terminal"""
# pylint: disable=import-outside-toplevel
from openbb_terminal import terminal_controller
if queue:
return terminal_controller.main(debug, queue, module="")
return terminal_controller.main(debug, dev, queue, module="")
terminal_controller.parse_args_and_run()
@@ -170,8 +176,13 @@ def main(
welcome: bool = True,
prompt: bool = True,
queue: Optional[List[str]] = None,
dev: bool = False,
):
"""Main function"""
if dev:
set_system_variable("DEV_BACKEND", True)
BackendEnvironment.BASE_URL = "https://payments.openbb.dev/"
BackendEnvironment.HUB_URL = "https://my.openbb.dev/"
local_session = Local.get_session() if session is None else session

View File

@@ -93,7 +93,7 @@ def login(session: Dict, remember: bool = False) -> LoginStatus:
preferences=get_current_user().preferences,
sources=SourcesModel(),
)
response = Hub.fetch_user_configs(session)
response = Hub.fetch_user_configs(session, Hub.BackendEnvironment.BASE_URL)
if response is not None:
if response.status_code == 200:
configs = json.loads(response.content)
@@ -159,6 +159,7 @@ def update_backend_sources(auth_header, configs, silent: bool = True):
key="features_sources",
value=updated_sources,
auth_header=auth_header,
base_url=Hub.BackendEnvironment.BASE_URL,
silent=silent,
)
except Exception:
@@ -190,7 +191,7 @@ def logout(
success = True
r = Hub.delete_session(auth_header, token)
r = Hub.delete_session(auth_header, token, base_url=Hub.BackendEnvironment.BASE_URL)
if not r or r.status_code != 200:
success = False

View File

@@ -56,7 +56,6 @@ class EconometricsController(BaseController):
"type",
"desc",
"corr",
"season",
"index",
"clean",
"add",
@@ -165,7 +164,7 @@ class EconometricsController(BaseController):
}
if session and get_current_user().preferences.USE_PROMPT_TOOLKIT:
choices: dict = {c: {} for c in self.controller_choices}
choices: dict = self.choices_default
choices["load"] = {
"--file": {c: {} for c in self.DATA_FILES},
"-f": "--file",
@@ -188,9 +187,9 @@ class EconometricsController(BaseController):
"granger",
"coint",
"corr",
"season",
"lag",
"vif",
"panel",
]:
choices[feature] = dict()
@@ -216,7 +215,6 @@ class EconometricsController(BaseController):
"norm",
"root",
"coint",
"season",
"lag",
"regressions",
"ols",
@@ -279,7 +277,6 @@ class EconometricsController(BaseController):
mt.add_cmd("type", self.files)
mt.add_cmd("desc", self.files)
mt.add_cmd("corr", self.files)
mt.add_cmd("season", self.files)
mt.add_cmd("index", self.files)
mt.add_cmd("clean", self.files)
mt.add_cmd("add", self.files)
@@ -752,79 +749,6 @@ class EconometricsController(BaseController):
ns_parser.export,
)
@log_start_end(log=logger)
def call_season(self, other_args: List[str]):
"""Process season command"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="season",
description="The seasonality for a given column",
)
parser.add_argument(
"-v",
"--values",
help="Dataset.column values to be displayed in a plot",
dest="values",
choices={
f"{dataset}.{column}": {column: None, dataset: None}
for dataset, dataframe in self.datasets.items()
for column in dataframe.columns
},
type=str,
)
parser.add_argument(
"-m",
help="A time lag to highlight on the plot",
dest="m",
type=int,
default=None,
)
parser.add_argument(
"--max_lag",
help="The maximal lag order to consider",
dest="max_lag",
type=int,
default=24,
)
parser.add_argument(
"-a",
"--alpha",
help="The confidence interval to display",
dest="alpha",
type=float,
default=0.05,
)
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "-v")
ns_parser = self.parse_known_args_and_warn(
parser, other_args, export_allowed=EXPORT_ONLY_FIGURES_ALLOWED
)
if not ns_parser:
return
if not ns_parser.values:
console.print("[red]Please enter valid dataset.\n[/red]")
return
try:
dataset, col = ns_parser.values.split(".")
data = self.datasets[dataset]
data.name = dataset
except ValueError:
console.print("[red]Please enter 'dataset'.'column'.[/red]\n")
return
econometrics_view.display_seasonality(
data=data,
column=col,
export=ns_parser.export,
m=ns_parser.m,
max_lag=ns_parser.max_lag,
alpha=ns_parser.alpha,
)
@log_start_end(log=logger)
def call_type(self, other_args: List[str]):
"""Process type command"""

View File

@@ -200,63 +200,6 @@ def display_corr(
return fig.show(external=external_axes)
@log_start_end(log=logger)
def display_seasonality(
data: pd.DataFrame,
column: str = "close",
export: str = "",
sheet_name: Optional[str] = None,
m: Optional[int] = None,
max_lag: int = 24,
alpha: float = 0.05,
external_axes: bool = False,
) -> Union[OpenBBFigure, None]:
"""Plot seasonality from a dataset
Parameters
----------
data: pd.DataFrame
The dataframe to plot
column: str
The column of the dataframe to analyze
sheet_name: str
Optionally specify the name of the sheet the data is exported to.
export: str
Format to export image
m: Optional[int]
Optionally, a time lag to highlight on the plot. Default is none.
max_lag: int
The maximal lag order to consider. Default is 24.
alpha: float
The confidence interval to display. Default is 0.05.
external_axes : bool, optional
Whether to return the figure object or not, by default False
"""
if data.empty:
return console.print("No data to plot")
series = data[column]
ending = get_ending(data.name, column)
fig = OpenBBFigure()
fig.set_title(f"Seasonality{ending}", wrap=True, wrap_width=55)
fig.add_corr_plot(series, m=m, max_lag=max_lag, alpha=alpha)
fig.update_xaxes(autorange=False, range=[-1, max_lag + 1])
fig.add_legend_label()
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"plot",
sheet_name=sheet_name,
figure=fig,
)
return fig.show(external=external_axes)
@log_start_end(log=logger)
def display_norm(
data: pd.Series,

View File

@@ -83,7 +83,7 @@ class QaController(StockBaseController):
self.resolution = "" # For the views
if session and get_current_user().preferences.USE_PROMPT_TOOLKIT:
choices: dict = {c: {} for c in self.controller_choices}
choices: dict = self.choices_default
choices["pick"] = {c: {} for c in self.options}
choices["unitroot"] = {
"--fuller_reg": {c: {} for c in self.FULLER_REG},

View File

@@ -646,7 +646,6 @@ def get_index(
end=end_date,
interval=interval,
progress=False,
show_errors=False,
ignore_tz=True,
)

View File

@@ -66,7 +66,7 @@ class FeatureFlagsController(BaseController):
super().__init__(queue)
if session and get_current_user().preferences.USE_PROMPT_TOOLKIT:
choices: dict = {c: {} for c in self.controller_choices}
choices: dict = self.choices_default
self.completer = NestedCompleter.from_nested_dict(choices)
def print_help(self):

View File

@@ -1115,8 +1115,8 @@ class ForecastController(BaseController):
description="The seasonality for a given column",
)
parser.add_argument(
"-v",
"--values",
"-d",
"--dataset",
help="Dataset.column values to be displayed in a plot",
dest="values",
choices=self.get_dataset_columns(),

View File

@@ -16,7 +16,29 @@ def get_historical(
source: Optional[str] = "YahooFinance",
expiry: Optional[str] = "",
) -> pd.DataFrame:
"""Get historical futures data"""
"""Get historical futures data
Parameters
----------
symbols : Union[str, List[str]]
The futures symbols you want to retrieve historical data for.
It can be either a single symbol as a string or a list of symbols.
start_date : str
The start date of the historical data you want to retrieve. The date should be in the format "YYYY-MM-DD".
end_date : str
The end date of the historical data you want to retrieve. The date should be in the format "YYYY-MM-DD".
source : Optional[str], default "YahooFinance"
The source from which you want to retrieve the historical data.
Valid values for the source are "YahooFinance" and "DataBento".
expiry : Optional[str], default ""
The expiry date for futures contracts. This parameter is optional and defaults to an empty string.
It is applicable only when the source is "YahooFinance" and the symbols are futures contracts.
Returns
-------
pd.DataFrame
A pandas DataFrame containing the historical data for the given symbols and date range.
"""
if source == "YahooFinance":
if isinstance(symbols, str):
symbols = [symbols]

View File

@@ -8,7 +8,7 @@ import logging
from typing import Callable, Dict, List, Optional
from openbb_terminal import keys_model, keys_view
from openbb_terminal.core.session.constants import KEYS_URL
from openbb_terminal.core.session.constants import BackendEnvironment
from openbb_terminal.core.session.current_user import get_current_user, is_local
from openbb_terminal.core.session.env_handler import get_reading_order
from openbb_terminal.custom_prompt_toolkit import NestedCompleter
@@ -75,7 +75,11 @@ class KeysController(BaseController): # pylint: disable=too-many-public-methods
"""
reading_order = get_reading_order()
hierarchy = "\n ".join(list(map(str, reversed(reading_order))))
return hierarchy if is_local() else f"{KEYS_URL}\n {hierarchy}"
return (
hierarchy
if is_local()
else f"{BackendEnvironment.HUB_URL + 'app/terminal/api-keys'}\n {hierarchy}"
)
def print_help(self, update_status: bool = True, reevaluate: bool = True):
"""Print help"""

View File

@@ -29,8 +29,8 @@ from openbb_terminal.account.show_prompt import get_show_prompt
from openbb_terminal.core.completer.choices import build_controller_choice_map
from openbb_terminal.core.config.paths import HIST_FILE_PATH
from openbb_terminal.core.session import hub_model as Hub
from openbb_terminal.core.session.constants import SCRIPT_TAGS
from openbb_terminal.core.session.current_user import get_current_user, is_local
from openbb_terminal.core.session.routines_handler import read_routine
from openbb_terminal.cryptocurrency import cryptocurrency_helpers
from openbb_terminal.custom_prompt_toolkit import NestedCompleter
from openbb_terminal.decorators import log_start_end
@@ -85,6 +85,7 @@ SUPPORT_TYPE = ["bug", "suggestion", "question", "generic"]
# TODO: We should try to avoid these global variables
RECORD_SESSION = False
RECORD_SESSION_LOCAL_ONLY = False
SESSION_RECORDED = list()
SESSION_RECORDED_NAME = ""
SESSION_RECORDED_DESCRIPTION = ""
@@ -111,7 +112,6 @@ class BaseController(metaclass=ABCMeta):
"reset",
"support",
"wiki",
"record",
"stop",
"screenshot",
"askobb",
@@ -892,8 +892,9 @@ class BaseController(metaclass=ABCMeta):
action="store",
dest="name",
type=str,
default=datetime.now().strftime("%Y%m%d_%H%M%S_routine.openbb"),
help="Routine file name to be saved.",
default="",
help="Routine title name to be saved - only use characters, digits and whitespaces.",
nargs="+",
)
parser.add_argument(
"-d",
@@ -901,15 +902,30 @@ class BaseController(metaclass=ABCMeta):
type=str,
dest="description",
help="The description of the routine",
default=f"Routine recorded at {datetime.now().strftime('%H:%M')} from the OpenBB Terminal",
nargs="+",
)
parser.add_argument(
"--tag1",
type=str,
dest="tag1",
help=f"The tag associated with the routine. Select from: {', '.join(SCRIPT_TAGS)}",
default="",
nargs="+",
)
parser.add_argument(
"-t",
"--tags",
"--tag2",
type=str,
dest="tags",
help="The tags of the routine",
dest="tag2",
help=f"The tag associated with the routine. Select from: {', '.join(SCRIPT_TAGS)}",
default="",
nargs="+",
)
parser.add_argument(
"--tag3",
type=str,
dest="tag3",
help=f"The tag associated with the routine. Select from: {', '.join(SCRIPT_TAGS)}",
default="",
nargs="+",
)
@@ -921,32 +937,107 @@ class BaseController(metaclass=ABCMeta):
help="Whether the routine should be public or not",
default=False,
)
parser.add_argument(
"-l",
"--local",
dest="local",
action="store_true",
help="Only save the routine locally - this is necessary if you are running terminal in guest mode.",
default=False,
)
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "-n")
ns_parser = self.parse_simple_args(parser, other_args)
if ns_parser:
if not ns_parser.name:
console.print(
"[red]Set a routine title by using the '-n' flag. E.g. 'record -n Morning routine'[/red]"
)
return
tag1 = (
" ".join(ns_parser.tag1)
if isinstance(ns_parser.tag1, list)
else ns_parser.tag1
)
if tag1 and tag1 not in SCRIPT_TAGS:
console.print(
f"[red]The parameter 'tag1' needs to be one of the following {', '.join(SCRIPT_TAGS)}[/red]"
)
return
tag2 = (
" ".join(ns_parser.tag2)
if isinstance(ns_parser.tag2, list)
else ns_parser.tag2
)
if tag2 and tag2 not in SCRIPT_TAGS:
console.print(
f"[red]The parameter 'tag2' needs to be one of the following {', '.join(SCRIPT_TAGS)}[/red]"
)
return
tag3 = (
" ".join(ns_parser.tag3)
if isinstance(ns_parser.tag3, list)
else ns_parser.tag3
)
if tag3 and tag3 not in SCRIPT_TAGS:
console.print(
f"[red]The parameter 'tag3' needs to be one of the following {', '.join(SCRIPT_TAGS)}[/red]"
)
return
if is_local() and not ns_parser.local:
console.print(
"[red]Recording session to the OpenBB Hub is not supported in guest mode.[/red]"
)
console.print(
"\n[yellow]Sign to OpenBB Hub to register: http://openbb.co[/yellow]"
)
console.print(
"\n[yellow]Otherwise set the flag '-l' to save the file locally.[/yellow]"
)
return
# Check if title has a valid format
title = " ".join(ns_parser.name) if ns_parser.name else ""
pattern = re.compile(r"^[a-zA-Z0-9\s]+$")
if not pattern.match(title):
console.print(
f"[red]Title '{title}' has invalid format. Please use only digits, characters and whitespaces.[/]"
)
return
global RECORD_SESSION
global RECORD_SESSION_LOCAL_ONLY
global SESSION_RECORDED_NAME
global SESSION_RECORDED_DESCRIPTION
global SESSION_RECORDED_TAGS
global SESSION_RECORDED_PUBLIC
SESSION_RECORDED_NAME = (
ns_parser.name
if ".openbb" in ns_parser.name
else ns_parser.name + ".openbb"
RECORD_SESSION = True
RECORD_SESSION_LOCAL_ONLY = ns_parser.local
SESSION_RECORDED_NAME = title
SESSION_RECORDED_DESCRIPTION = (
" ".join(ns_parser.description)
if isinstance(ns_parser.description, list)
else ns_parser.description
)
SESSION_RECORDED_TAGS = tag1 if tag1 else ""
SESSION_RECORDED_TAGS += "," + tag2 if tag2 else ""
SESSION_RECORDED_TAGS += "," + tag3 if tag3 else ""
SESSION_RECORDED_DESCRIPTION = " ".join(ns_parser.description)
SESSION_RECORDED_TAGS = " ".join(ns_parser.tags) if ns_parser.tags else ""
SESSION_RECORDED_PUBLIC = ns_parser.public
console.print(
"[green]The session is successfully being recorded."
+ " Remember to 'stop' before exiting terminal!\n[/green]"
f"[green]The routine '{title}' is successfully being recorded.[/green]"
)
console.print(
"\n[yellow]Remember to run 'stop' command when you are done!\n[/yellow]"
)
RECORD_SESSION = True
@log_start_end(log=logger)
def call_stop(self, _) -> None:
@@ -956,57 +1047,102 @@ class BaseController(metaclass=ABCMeta):
if not RECORD_SESSION:
console.print(
"[red]There is no session being recorded. Start one using 'record'[/red]\n"
"[red]There is no session being recorded. Start one using the command 'record'[/red]\n"
)
elif not SESSION_RECORDED:
elif len(SESSION_RECORDED) < 5:
console.print(
"[red]There is no session to be saved. Run at least 1 command after starting 'record'[/red]\n"
"[red]Run at least 4 commands before stopping recording a session.[/red]\n"
)
else:
current_user = get_current_user()
routine_file = os.path.join(
current_user.preferences.USER_ROUTINES_DIRECTORY, SESSION_RECORDED_NAME
)
if os.path.isfile(routine_file):
routine_file = os.path.join(
current_user.preferences.USER_ROUTINES_DIRECTORY,
datetime.now().strftime("%Y%m%d_%H%M%S_") + SESSION_RECORDED_NAME,
# Check if the user just wants to store routine locally
# This works regardless of whether they are logged in or not
if RECORD_SESSION_LOCAL_ONLY:
# Whitespaces are replaced by underscores and an .openbb extension is added
title_for_local_storage = (
SESSION_RECORDED_NAME.replace(" ", "_") + ".openbb"
)
# Writing to file
with open(routine_file, "w") as file1:
# Writing data to a file
file1.writelines([c + "\n\n" for c in SESSION_RECORDED[:-1]])
routine_file = os.path.join(
current_user.preferences.USER_ROUTINES_DIRECTORY,
title_for_local_storage,
)
console.print(
f"[green]Your routine has been recorded and saved here: {routine_file}[/green]\n"
)
if not is_local():
routine = read_routine(file_name=routine_file)
if routine is not None:
name = SESSION_RECORDED_NAME.split(sep=".openbb", maxsplit=-1)[0]
kwargs = {
"auth_header": current_user.profile.get_auth_header(),
"name": name,
"description": SESSION_RECORDED_DESCRIPTION,
"routine": routine,
"tags": SESSION_RECORDED_TAGS,
"public": SESSION_RECORDED_PUBLIC,
}
response = Hub.upload_routine(**kwargs) # type: ignore
if response is not None and response.status_code == 409:
i = console.input(
"A routine with the same name already exists, "
"do you want to replace it? (y/n): "
)
# If file already exists, add a timestamp to the name
if os.path.isfile(routine_file):
i = console.input(
"A local routine with the same name already exists, "
"do you want to override it? (y/n): "
)
console.print("")
while i.lower() not in ["y", "yes", "n", "no"]:
i = console.input("Select 'y' or 'n' to proceed: ")
console.print("")
if i.lower() in ["y", "yes"]:
kwargs["override"] = True # type: ignore
response = Hub.upload_routine(**kwargs) # type: ignore
else:
console.print("[info]Aborted.[/info]")
if i.lower() in ["n", "no"]:
new_name = (
datetime.now().strftime("%Y%m%d_%H%M%S_")
+ title_for_local_storage
)
routine_file = os.path.join(
current_user.preferences.USER_ROUTINES_DIRECTORY,
new_name,
)
console.print(
f"[yellow]The routine name has been updated to '{new_name}'[/yellow]\n"
)
# Writing to file
with open(routine_file, "w") as file1:
lines = ["# OpenBB Terminal - Routine", "\n"]
username = get_current_user().profile.username
lines += [f"# Author: {username}", "\n\n"] if username else ["\n"]
lines += [
f"# Title: {SESSION_RECORDED_NAME}",
"\n",
f"# Tags: {SESSION_RECORDED_TAGS}",
"\n\n",
f"# Description: {SESSION_RECORDED_DESCRIPTION}",
"\n\n",
]
lines += [c + "\n" for c in SESSION_RECORDED[:-1]]
# Writing data to a file
file1.writelines(lines)
console.print(
f"[green]Your routine has been recorded and saved here: {routine_file}[/green]\n"
)
# If user doesn't specify they want to store routine locally
else:
# Confirm that the user is logged in
if not is_local():
# routine = read_routine(file_name=routine_file)
routine = "\n".join(SESSION_RECORDED[:-1])
if routine is not None:
kwargs = {
"auth_header": current_user.profile.get_auth_header(),
"name": SESSION_RECORDED_NAME,
"description": SESSION_RECORDED_DESCRIPTION,
"routine": routine,
"tags": SESSION_RECORDED_TAGS,
"public": SESSION_RECORDED_PUBLIC,
"base_url": Hub.BackendEnvironment.BASE_URL,
}
response = Hub.upload_routine(**kwargs) # type: ignore
if response is not None and response.status_code == 409:
i = console.input(
"A routine with the same name already exists, "
"do you want to replace it? (y/n): "
)
console.print("")
if i.lower() in ["y", "yes"]:
kwargs["override"] = True # type: ignore
response = Hub.upload_routine(**kwargs) # type: ignore
else:
console.print("[info]Aborted.[/info]")
# Clear session to be recorded again
RECORD_SESSION = False

View File

@@ -43,7 +43,7 @@ class DegiroController(BaseController):
self.__degiro_view = DegiroView()
if session and get_current_user().preferences.USE_PROMPT_TOOLKIT:
choices: dict = {c: {} for c in self.controller_choices}
choices: dict = self.choices_default
zero_to_hundred: dict = {str(c): {} for c in range(0, 100)}

View File

@@ -4,7 +4,6 @@ __docformat__ = "numpy"
import datetime
import logging
import warnings
from os import environ
from typing import Any, Dict
import numpy as np
@@ -12,6 +11,7 @@ import pandas as pd
import yfinance as yf
from tqdm import tqdm
from openbb_terminal.core.session.current_system import get_current_system
from openbb_terminal.decorators import log_start_end
from openbb_terminal.portfolio.allocation_model import get_allocation
from openbb_terminal.portfolio.portfolio_helper import (
@@ -196,7 +196,7 @@ class PortfolioEngine:
# Load transactions from file
if path.endswith(".xlsx"):
if str(environ.get("DEBUG_MODE", "false")).lower() != "true":
if not get_current_system().DEBUG_MODE:
warnings.filterwarnings(
"ignore", category=UserWarning, module="openpyxl"
)

View File

@@ -78,7 +78,7 @@ class ParametersController(BaseController):
self.DATA_FILES = params_helpers.load_data_files()
if session and get_current_user().preferences.USE_PROMPT_TOOLKIT:
choices: dict = {c: {} for c in self.controller_choices}
choices: dict = self.choices_default
choices["set"] = {c: None for c in self.models}
choices["set"]["--model"] = {c: None for c in self.models}
choices["set"]["-m"] = "--model"

View File

@@ -21,7 +21,7 @@ from openbb_terminal.core.config.paths import (
SETTINGS_ENV_FILE,
STYLES_DIRECTORY_REPO,
)
from openbb_terminal.core.session.constants import CHARTS_TABLES_URL, COLORS_URL
from openbb_terminal.core.session.constants import BackendEnvironment
from openbb_terminal.core.session.current_user import (
get_current_user,
is_local,
@@ -231,7 +231,7 @@ class SettingsController(BaseController):
" Then, place the downloaded file 'openbb_config.richstyle.json'"
f" inside {get_current_user().preferences.USER_STYLES_DIRECTORY} or "
f"{STYLES_DIRECTORY_REPO}. If you have a hub account you can change colors "
f"here {COLORS_URL}.",
f"here {BackendEnvironment.HUB_URL + 'app/terminal/theme'}.",
)
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "-s")
@@ -246,6 +246,7 @@ class SettingsController(BaseController):
value=ns_parser.style,
type_="settings",
auth_header=get_current_user().profile.get_auth_header(),
base_url=BackendEnvironment.HUB_URL,
)
console.print("")
console.print("Colors updated.")
@@ -266,7 +267,7 @@ class SettingsController(BaseController):
dest="style",
choices=["dark", "light"],
help="Choose chart style. If you have a hub account you can change theme "
f"here {CHARTS_TABLES_URL}.",
f"here {BackendEnvironment.HUB_URL + 'app/terminal/theme/charts-tables'}.",
required="-h" not in other_args and "--help" not in other_args,
)
if other_args and "-" not in other_args[0][0]:
@@ -282,6 +283,7 @@ class SettingsController(BaseController):
value=ns_parser.style,
type_="terminal_style",
auth_header=get_current_user().profile.get_auth_header(),
base_url=BackendEnvironment.HUB_URL,
)
console.print("")
theme.apply_style(ns_parser.style)
@@ -303,7 +305,7 @@ class SettingsController(BaseController):
dest="style",
choices=["dark", "light"],
help="Choose table style. If you have a hub account you can change theme "
f"here {CHARTS_TABLES_URL}.",
f"here {BackendEnvironment.HUB_URL + 'app/terminal/theme/charts-tables'}.",
required="-h" not in other_args and "--help" not in other_args,
)
if other_args and "-" not in other_args[0][0]:
@@ -319,6 +321,7 @@ class SettingsController(BaseController):
value=ns_parser.style,
type_="terminal_style",
auth_header=get_current_user().profile.get_auth_header(),
base_url=BackendEnvironment.HUB_URL,
)
console.print("")
console.print("Table theme updated.\n")

View File

@@ -8,7 +8,7 @@ import os
from pathlib import Path
from typing import List, Optional
from openbb_terminal.core.session.constants import SOURCES_URL
from openbb_terminal.core.session.constants import BackendEnvironment
# IMPORTATION THIRDPARTY
# IMPORTATION INTERNAL
@@ -54,7 +54,7 @@ class SourcesController(BaseController):
if Path(sources_file).exists() and os.stat(sources_file).st_size > 0:
self.source = sources_file
else:
self.source = SOURCES_URL
self.source = BackendEnvironment.HUB_URL + "app/terminal/data-sources"
def parse_input(self, an_input: str) -> List:
"""Parse controller input
@@ -176,6 +176,7 @@ class SourcesController(BaseController):
key="features_sources",
value=choices,
auth_header=get_current_user().profile.get_auth_header(),
base_url=BackendEnvironment.BASE_URL,
)
console.print("")
console.print(

View File

@@ -47,7 +47,6 @@ class BehaviouralAnalysisController(StockBaseController):
"load",
"wsb",
"popular",
"spacc",
"getdd",
"redditsent",
"bullbear",
@@ -97,7 +96,6 @@ class BehaviouralAnalysisController(StockBaseController):
mt.add_cmd("snews", self.ticker)
mt.add_cmd("wsb")
mt.add_cmd("popular")
mt.add_cmd("spacc")
mt.add_cmd("getdd")
mt.add_cmd("redditsent", self.ticker)
mt.add_cmd("trending")
@@ -137,40 +135,6 @@ class BehaviouralAnalysisController(StockBaseController):
symbol=self.ticker, export=ns_parser.export
)
@log_start_end(log=logger)
def call_spacc(self, other_args: List[str]):
"""Process spacc command."""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="spacc",
description="""Print other users SPACs announcement under subreddit 'SPACs'. [Source: Reddit]""",
)
parser.add_argument(
"-l",
"--limit",
action="store",
dest="n_limit",
type=check_positive,
default=10,
help="limit of posts with SPACs retrieved",
)
parser.add_argument(
"-p",
"--popular",
action="store_true",
default=False,
dest="b_popular",
help="popular flag, if true the posts retrieved are based on score rather than time",
)
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "-l")
ns_parser = self.parse_known_args_and_warn(parser, other_args)
if ns_parser:
reddit_view.display_spac_community(
limit=ns_parser.n_limit, popular=ns_parser.b_popular
)
@log_start_end(log=logger)
def call_wsb(self, other_args: List[str]):
"""Process wsb command."""

View File

@@ -442,7 +442,10 @@ class ComparisonAnalysisController(BaseController):
other_args.insert(0, "-s")
ns_parser = self.parse_known_args_and_warn(parser, other_args)
if ns_parser:
self.similar = list(set(ns_parser.l_similar + [self.ticker.upper()]))
if self.ticker:
self.similar = list(set(ns_parser.l_similar + [self.ticker.upper()]))
else:
self.similar = list(set(ns_parser.l_similar))
self.user = "Custom"
console.print(
f"[{self.user}] Similar Companies: {', '.join(self.similar)}", "\n"

View File

@@ -275,8 +275,8 @@ def get_government_buys(
else -float(x["min"]),
axis=1,
)
df_gov = df_gov.sort_values("TransactionDate", ascending=True)
df_gov = df_gov[df_gov.Transaction == "Purchase"]
df_gov = df_gov.sort_values(by="Amount", ascending=False).reset_index(drop=True)
return df_gov
@@ -348,7 +348,11 @@ def get_government_sells(
)
df_gov = df_gov.sort_values("TransactionDate", ascending=True)
df_gov = (
df_gov[df_gov.Transaction == "Sale"]
.sort_values(by="Amount", key=lambda x: abs(x), ascending=False)
.reset_index(drop=True)
)
return df_gov

View File

@@ -320,8 +320,8 @@ def display_vol_surface(
tick_kwargs = dict(tickfont=dict(size=13), titlefont=dict(size=16))
fig.update_layout(
scene=dict(
xaxis=dict(title="Strike", **tick_kwargs),
yaxis=dict(title="DTE", **tick_kwargs),
xaxis=dict(title="DTE", **tick_kwargs),
yaxis=dict(title="Strike", **tick_kwargs),
zaxis=dict(title=z, **tick_kwargs),
)
)

View File

@@ -20,7 +20,6 @@ import certifi
import pandas as pd
import requests
from prompt_toolkit import PromptSession
from prompt_toolkit.completion import NestedCompleter
from prompt_toolkit.formatted_text import HTML
from prompt_toolkit.styles import Style
@@ -34,9 +33,10 @@ from openbb_terminal.core.config.paths import (
SETTINGS_ENV_FILE,
)
from openbb_terminal.core.log.generation.custom_logger import log_terminal
from openbb_terminal.core.session import session_controller
from openbb_terminal.core.session import constants, session_controller
from openbb_terminal.core.session.current_system import set_system_variable
from openbb_terminal.core.session.current_user import get_current_user, set_preference
from openbb_terminal.custom_prompt_toolkit import NestedCompleter
from openbb_terminal.helper_funcs import (
EXPORT_ONLY_RAW_DATA_ALLOWED,
check_positive,
@@ -90,6 +90,7 @@ class TerminalController(BaseController):
"news",
"intro",
"askobb",
"record",
]
CHOICES_MENUS = [
"stocks",
@@ -142,44 +143,10 @@ class TerminalController(BaseController):
def update_runtime_choices(self):
"""Update runtime choices."""
self.ROUTINE_FILES = {
filepath.name: filepath
for filepath in get_current_user().preferences.USER_ROUTINES_DIRECTORY.rglob(
"*.openbb"
)
}
if get_current_user().profile.get_token():
self.ROUTINE_DEFAULT_FILES = {
filepath.name: filepath
for filepath in Path(
get_current_user().preferences.USER_ROUTINES_DIRECTORY
/ "hub"
/ "default"
).rglob("*.openbb")
}
self.ROUTINE_PERSONAL_FILES = {
filepath.name: filepath
for filepath in Path(
get_current_user().preferences.USER_ROUTINES_DIRECTORY
/ "hub"
/ "personal"
).rglob("*.openbb")
}
self.ROUTINE_CHOICES["--file"] = {
filename: None for filename in self.ROUTINE_FILES
}
self.ROUTINE_CHOICES["--example"] = None
self.ROUTINE_CHOICES["-e"] = None
self.ROUTINE_CHOICES["--input"] = None
self.ROUTINE_CHOICES["-i"] = None
self.ROUTINE_CHOICES["--help"] = None
self.ROUTINE_CHOICES["--h"] = None
if session and get_current_user().preferences.USE_PROMPT_TOOLKIT:
# choices: dict = self.choices_default
choices: dict = {c: {} for c in self.controller_choices} # type: ignore
choices["support"] = self.SUPPORT_CHOICES
choices["exe"] = self.ROUTINE_CHOICES
choices["news"] = self.NEWS_CHOICES
choices["news"]["--source"] = {c: {} for c in ["Biztoc", "Feedparser"]}
choices["hold"] = {c: None for c in ["on", "off", "-s", "--sameaxis"]}
@@ -187,6 +154,56 @@ class TerminalController(BaseController):
if biztoc_model.BIZTOC_TAGS:
choices["news"]["--tag"] = {c: {} for c in biztoc_model.BIZTOC_TAGS}
self.ROUTINE_FILES = {
filepath.name: filepath
for filepath in get_current_user().preferences.USER_ROUTINES_DIRECTORY.rglob(
"*.openbb"
)
}
if get_current_user().profile.get_token():
self.ROUTINE_DEFAULT_FILES = {
filepath.name: filepath
for filepath in Path(
get_current_user().preferences.USER_ROUTINES_DIRECTORY
/ "hub"
/ "default"
).rglob("*.openbb")
}
self.ROUTINE_PERSONAL_FILES = {
filepath.name: filepath
for filepath in Path(
get_current_user().preferences.USER_ROUTINES_DIRECTORY
/ "hub"
/ "personal"
).rglob("*.openbb")
}
choices["exe"] = {
"--file": {
filename: {} for filename in list(self.ROUTINE_FILES.keys())
},
"-f": "--file",
"--example": None,
"-e": "--example",
"--input": None,
"-i": "--input",
"--url": None,
}
choices["record"] = {
"--name": None,
"-n": "--name",
"--description": None,
"-d": "--description",
"--public": None,
"-p": "--public",
"--local": None,
"-l": "--local",
"--tag1": {c: None for c in constants.SCRIPT_TAGS},
"--tag2": {c: None for c in constants.SCRIPT_TAGS},
"--tag3": {c: None for c in constants.SCRIPT_TAGS},
}
self.completer = NestedCompleter.from_nested_dict(choices)
def print_help(self):
@@ -1084,6 +1101,7 @@ def run_routine(file: str, routines_args=Optional[str]):
def main(
debug: bool,
dev: bool,
path_list: List[str],
routines_args: Optional[List[str]] = None,
**kwargs,
@@ -1094,6 +1112,8 @@ def main(
----------
debug : bool
Whether to run the terminal in debug mode
dev:
Points backend towards development environment instead of production
test : bool
Whether to run the terminal in integrated test mode
filtert : str
@@ -1113,6 +1133,11 @@ def main(
if debug:
set_system_variable("DEBUG_MODE", True)
if dev:
set_system_variable("DEV_BACKEND", True)
constants.BackendEnvironment.BASE_URL = "https://payments.openbb.dev/"
constants.BackendEnvironment.HUB_URL = "https://my.openbb.dev/"
cfg.start_plot_backend()
if isinstance(path_list, list) and path_list[0].endswith(".openbb"):
@@ -1140,6 +1165,13 @@ def parse_args_and_run():
default=False,
help="Runs the terminal in debug mode.",
)
parser.add_argument(
"--dev",
dest="dev",
action="store_true",
default=False,
help="Points backend towards development environment instead of production",
)
parser.add_argument(
"--file",
help="The path or .openbb file to run.",
@@ -1211,6 +1243,7 @@ def parse_args_and_run():
main(
ns_parser.debug,
ns_parser.dev,
ns_parser.path,
ns_parser.routine_args,
module=ns_parser.module,

View File

@@ -23,7 +23,7 @@ from openbb_terminal import thought_of_the_day as thought
from openbb_terminal.base_helpers import load_env_files
from openbb_terminal.core.config.paths import HIST_FILE_PATH, SETTINGS_ENV_FILE
from openbb_terminal.core.plots.backend import plots_backend
from openbb_terminal.core.session.constants import REGISTER_URL
from openbb_terminal.core.session.constants import BackendEnvironment
from openbb_terminal.core.session.current_system import get_current_system
from openbb_terminal.core.session.current_user import (
get_current_user,
@@ -257,7 +257,8 @@ def print_guest_block_msg():
console.print(
"[info]You are currently logged as a guest.[/info]\n"
"[info]Login to use this feature.[/info]\n\n"
f"[info]If you don't have an account, you can create one here: [/info][cmds]{REGISTER_URL}\n[/cmds]"
"[info]If you don't have an account, you can create one here: [/info]"
f"[cmds]{BackendEnvironment.HUB_URL + 'register'}\n[/cmds]"
)
@@ -369,6 +370,7 @@ def reset(queue: Optional[List[str]] = None):
plots_backend().close(reset=True)
load_env_files()
debug = get_current_system().DEBUG_MODE
dev = get_current_system().DEV_BACKEND
try:
# save the current user
@@ -399,7 +401,7 @@ def reset(queue: Optional[List[str]] = None):
if is_local():
from openbb_terminal.terminal_controller import main
main(debug, queue_list, module="") # type: ignore
main(debug, dev, queue_list, module="") # type: ignore
else:
from openbb_terminal.core.session import session_controller

8
poetry.lock generated vendored
View File

@@ -2298,13 +2298,13 @@ user_agent = "*"
[[package]]
name = "finvizfinance"
version = "0.14.5"
version = "0.14.6rc2"
description = "Finviz Finance. Information downloader."
optional = false
python-versions = ">=3.5"
files = [
{file = "finvizfinance-0.14.5-py3-none-any.whl", hash = "sha256:1b22bfd7af36f01a08c6032e19f64e810ae3f42bfba681e9a2132226a5a90924"},
{file = "finvizfinance-0.14.5.tar.gz", hash = "sha256:c97fe0e69d681d9108113aed943988561665d0d4dbd64ffa0b666a2899c25899"},
{file = "finvizfinance-0.14.6rc2-py3-none-any.whl", hash = "sha256:70742b5054e84fbe5a1ffcc7cad97ca9225292e07ce0349dcb78462d5e5e51a0"},
{file = "finvizfinance-0.14.6rc2.tar.gz", hash = "sha256:736f3064faac1f8ba6d06fdd7c77f79a3e831c2048fc6442759861fce4c4a6e8"},
]
[package.dependencies]
@@ -10495,4 +10495,4 @@ optimization = ["Riskfolio-Lib"]
[metadata]
lock-version = "2.0"
python-versions = "^3.8.1,<3.11, !=3.9.7"
content-hash = "10cccda4c8b51c57241490d53d46268092e6ffe9054c8ef22b7e58477ad3b98c"
content-hash = "cf28c86b1360b436443a486b5ed8b33101e35d3a6c02f81c7dc85515e520c634"

4
pyproject.toml vendored
View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "openbb"
version = "3.2.0"
version = "3.2.1"
description = "Investment Research for Everyone, Anywhere."
license = "MIT"
authors = ["Didier Rodrigues Lopes"]
@@ -42,7 +42,7 @@ robin-stocks = "^2.0.3"
pycoingecko = "^3.1.0"
detecta = "^0.0.5"
tradingview-ta = "^3.2.3"
finvizfinance = "^0.14.0"
finvizfinance = "0.14.6rc2"
statsmodels = "^0.13"
python-coinmarketcap = "^0.2"
oandapyV20 = "^0.6.3"

View File

@@ -98,7 +98,7 @@ filelock==3.12.2 ; python_full_version >= "3.8.1" and python_full_version != "3.
financedatabase==2.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
finnhub-python==2.4.18 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
finviz==1.4.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
finvizfinance==0.14.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
finvizfinance==0.14.6rc2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
flake8==6.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
flask==2.3.2 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7"
fonttools==4.41.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"

2
requirements.txt vendored
View File

@@ -65,7 +65,7 @@ ffn==0.3.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" a
financedatabase==2.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
finnhub-python==2.4.18 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
finviz==1.4.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
finvizfinance==0.14.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
finvizfinance==0.14.6rc2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
flake8==6.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
fonttools==4.41.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
formulaic==0.6.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"

View File

@@ -34,8 +34,9 @@ def main():
and ("--login" in sys.argv[1:] or is_installer())
and sys.stdin.isatty()
)
dev = "--dev" in sys.argv[1:]
session_controller.main(prompt=prompt_login)
session_controller.main(prompt=prompt_login, dev=dev)
if __name__ == "__main__":

View File

@@ -404,7 +404,10 @@ def test_call_list(mocker, test_user):
controller.call_list(other_args=["--page", "1", "--size", "10"])
mock_list_routines.assert_called_once_with(
auth_header="Bearer 123", page=1, size=10
auth_header="Bearer 123",
page=1,
size=10,
base_url="https://payments.openbb.co/",
)
@@ -446,6 +449,7 @@ def test_call_upload(mocker, test_user):
routine="do something",
tags="stocks",
public=False,
base_url="https://payments.openbb.co/",
)
@@ -484,6 +488,7 @@ def test_call_download(mocker, test_user):
mock_download_routine.assert_called_once_with(
auth_header="Bearer 123",
uuid="script1",
base_url="https://payments.openbb.co/",
)
mock_save_routine.assert_called_once_with(
file_name="script1.openbb", routine=["do something", "personal"]
@@ -555,6 +560,7 @@ def test_call_generate(mocker, monkeypatch, test_user):
mock_generate.assert_called_once_with(
auth_header="Bearer 123",
base_url="https://payments.openbb.co/",
days=30,
)

View File

@@ -46,7 +46,7 @@ def test_create_session_success(email, password):
assert response.json() == TEST_RESPONSE
mock_post.assert_called_once_with(
url=hub_model.BASE_URL + "login",
url=hub_model.BackendEnvironment.BASE_URL + "login",
json={"email": email, "password": password, "remember": True},
timeout=hub_model.TIMEOUT,
)
@@ -113,7 +113,7 @@ def test_create_session_from_token_success(mocker, token):
assert response.json() == TEST_RESPONSE
mock_post.assert_called_once_with(
url=hub_model.BASE_URL + "sdk/login",
url=hub_model.BackendEnvironment.BASE_URL + "sdk/login",
json={"token": token},
timeout=hub_model.TIMEOUT,
)
@@ -151,7 +151,7 @@ def test_delete_session_success(auth_header, token):
assert response.status_code == 200
mock_post.assert_called_once_with(
url=hub_model.BASE_URL + "logout",
url=hub_model.BackendEnvironment.BASE_URL + "logout",
headers={"Authorization": "Bearer test_token"},
json={"token": "test_token"},
timeout=hub_model.TIMEOUT,
@@ -233,7 +233,9 @@ def test_get_session_success():
) as create_session_mock:
result = hub_model.get_session("email", "password")
assert result == {"session": "info"}
create_session_mock.assert_called_once_with("email", "password")
create_session_mock.assert_called_once_with(
"email", "password", base_url="https://payments.openbb.co/"
)
def test_get_session_401():
@@ -246,7 +248,9 @@ def test_get_session_401():
) as create_session_mock:
result = hub_model.get_session("email", "password")
assert result == {}
create_session_mock.assert_called_once_with("email", "password")
create_session_mock.assert_called_once_with(
"email", "password", base_url="https://payments.openbb.co/"
)
def test_get_session_403():
@@ -259,7 +263,9 @@ def test_get_session_403():
) as create_session_mock:
result = hub_model.get_session("email", "password")
assert result == {}
create_session_mock.assert_called_once_with("email", "password")
create_session_mock.assert_called_once_with(
"email", "password", base_url="https://payments.openbb.co/"
)
def test_get_session_failed_to_request():
@@ -268,7 +274,9 @@ def test_get_session_failed_to_request():
) as create_session_mock:
result = hub_model.get_session("email", "password")
assert result == {}
create_session_mock.assert_called_once_with("email", "password")
create_session_mock.assert_called_once_with(
"email", "password", base_url="https://payments.openbb.co/"
)
def test_get_session_from_token():
@@ -282,7 +290,9 @@ def test_get_session_from_token():
) as create_session_mock:
result = hub_model.get_session_from_token("token")
assert result == {"session": "info"}
create_session_mock.assert_called_once_with("token")
create_session_mock.assert_called_once_with(
"token", base_url="https://payments.openbb.co/"
)
def test_get_session_from_token_401():
@@ -295,7 +305,9 @@ def test_get_session_from_token_401():
) as create_session_mock:
result = hub_model.get_session_from_token("token")
assert result == {}
create_session_mock.assert_called_once_with("token")
create_session_mock.assert_called_once_with(
"token", base_url="https://payments.openbb.co/"
)
def test_get_session_from_token_403():
@@ -308,7 +320,9 @@ def test_get_session_from_token_403():
) as create_session_mock:
result = hub_model.get_session_from_token("token")
assert result == {}
create_session_mock.assert_called_once_with("token")
create_session_mock.assert_called_once_with(
"token", base_url="https://payments.openbb.co/"
)
def test_get_session_from_token_failed_to_request():
@@ -318,7 +332,9 @@ def test_get_session_from_token_failed_to_request():
) as create_session_mock:
result = hub_model.get_session_from_token("token")
assert result == {}
create_session_mock.assert_called_once_with("token")
create_session_mock.assert_called_once_with(
"token", base_url="https://payments.openbb.co/"
)
@pytest.mark.parametrize("token_type, access_token", [("TokenType", "AccessToken")])
@@ -336,7 +352,7 @@ def test_fetch_user_configs_success(token_type, access_token):
assert result == mock_response
requests_get_mock.assert_called_once()
_, kwargs = requests_get_mock.call_args
assert kwargs["url"] == hub_model.BASE_URL + "terminal/user"
assert kwargs["url"] == hub_model.BackendEnvironment.BASE_URL + "terminal/user"
assert kwargs["headers"] == {
"Authorization": f"{token_type.title()} {access_token}"
}
@@ -352,7 +368,7 @@ def test_fetch_user_configs_failure():
result = hub_model.fetch_user_configs(session)
assert isinstance(result, requests.Response)
get_mock.assert_called_once_with(
url=hub_model.BASE_URL + "terminal/user",
url=hub_model.BackendEnvironment.BASE_URL + "terminal/user",
headers={"Authorization": "Bearer abc123"},
timeout=hub_model.TIMEOUT,
)
@@ -366,7 +382,7 @@ def test_fetch_user_configs_connection_error():
result = hub_model.fetch_user_configs(session)
assert result is None
get_mock.assert_called_once_with(
url=hub_model.BASE_URL + "terminal/user",
url=hub_model.BackendEnvironment.BASE_URL + "terminal/user",
headers={"Authorization": "Bearer abc123"},
timeout=hub_model.TIMEOUT,
)
@@ -378,7 +394,7 @@ def test_fetch_user_configs_timeout():
result = hub_model.fetch_user_configs(session)
assert result is None
get_mock.assert_called_once_with(
url=hub_model.BASE_URL + "terminal/user",
url=hub_model.BackendEnvironment.BASE_URL + "terminal/user",
headers={"Authorization": "Bearer abc123"},
timeout=hub_model.TIMEOUT,
)
@@ -390,7 +406,7 @@ def test_fetch_user_configs_exception():
result = hub_model.fetch_user_configs(session)
assert result is None
get_mock.assert_called_once_with(
url=hub_model.BASE_URL + "terminal/user",
url=hub_model.BackendEnvironment.BASE_URL + "terminal/user",
headers={"Authorization": "Bearer abc123"},
timeout=hub_model.TIMEOUT,
)
@@ -415,7 +431,7 @@ def test_upload_config_success(key, value, type_, auth_header):
assert result.status_code == mock_response.status_code
requests_patch_mock.assert_called_once()
_, kwargs = requests_patch_mock.call_args
assert kwargs["url"] == hub_model.BASE_URL + "terminal/user"
assert kwargs["url"] == hub_model.BackendEnvironment.BASE_URL + "terminal/user"
assert kwargs["headers"] == {"Authorization": f"{auth_header}"}
assert kwargs["json"] == {"key": f"features_{type_}.{key}", "value": value}
assert kwargs["timeout"] == hub_model.TIMEOUT
@@ -434,7 +450,7 @@ def test_upload_config_failure():
assert result.status_code == mock_response.status_code
requests_patch_mock.assert_called_once()
_, kwargs = requests_patch_mock.call_args
assert kwargs["url"] == hub_model.BASE_URL + "terminal/user"
assert kwargs["url"] == hub_model.BackendEnvironment.BASE_URL + "terminal/user"
assert kwargs["headers"] == {"Authorization": "auth_header"}
assert kwargs["json"] == {"key": "features_settings.key", "value": "value"}
assert kwargs["timeout"] == hub_model.TIMEOUT
@@ -451,7 +467,7 @@ def test_upload_config_connection_error():
assert result is None
requests_patch_mock.assert_called_once()
_, kwargs = requests_patch_mock.call_args
assert kwargs["url"] == hub_model.BASE_URL + "terminal/user"
assert kwargs["url"] == hub_model.BackendEnvironment.BASE_URL + "terminal/user"
assert kwargs["headers"] == {"Authorization": "auth_header"}
assert kwargs["json"] == {"key": "features_settings.key", "value": "value"}
assert kwargs["timeout"] == hub_model.TIMEOUT
@@ -468,7 +484,7 @@ def test_upload_config_timeout():
assert result is None
requests_patch_mock.assert_called_once()
_, kwargs = requests_patch_mock.call_args
assert kwargs["url"] == hub_model.BASE_URL + "terminal/user"
assert kwargs["url"] == hub_model.BackendEnvironment.BASE_URL + "terminal/user"
assert kwargs["headers"] == {"Authorization": "auth_header"}
assert kwargs["json"] == {"key": "features_settings.key", "value": "value"}
assert kwargs["timeout"] == hub_model.TIMEOUT
@@ -485,7 +501,7 @@ def test_upload_config_exception():
assert result is None
requests_patch_mock.assert_called_once()
_, kwargs = requests_patch_mock.call_args
assert kwargs["url"] == hub_model.BASE_URL + "terminal/user"
assert kwargs["url"] == hub_model.BackendEnvironment.BASE_URL + "terminal/user"
assert kwargs["headers"] == {"Authorization": "auth_header"}
assert kwargs["json"] == {"key": "features_settings.key", "value": "value"}
assert kwargs["timeout"] == hub_model.TIMEOUT
@@ -504,7 +520,7 @@ def test_clear_user_configs_success():
assert result.status_code == mock_response.status_code
requests_put_mock.assert_called_once()
_, kwargs = requests_put_mock.call_args
assert kwargs["url"] == hub_model.BASE_URL + "user"
assert kwargs["url"] == hub_model.BackendEnvironment.BASE_URL + "user"
assert kwargs["headers"] == {"Authorization": "auth_header"}
assert kwargs["json"] == {"config": {}}
assert kwargs["timeout"] == hub_model.TIMEOUT
@@ -523,7 +539,7 @@ def test_clear_user_configs_failure():
assert result.status_code == mock_response.status_code
requests_put_mock.assert_called_once()
_, kwargs = requests_put_mock.call_args
assert kwargs["url"] == hub_model.BASE_URL + "user"
assert kwargs["url"] == hub_model.BackendEnvironment.BASE_URL + "user"
assert kwargs["headers"] == {"Authorization": "auth_header"}
assert kwargs["json"] == {"config": {}}
assert kwargs["timeout"] == hub_model.TIMEOUT
@@ -539,7 +555,7 @@ def test_clear_user_configs_timeout():
assert result is None
requests_put_mock.assert_called_once()
_, kwargs = requests_put_mock.call_args
assert kwargs["url"] == hub_model.BASE_URL + "user"
assert kwargs["url"] == hub_model.BackendEnvironment.BASE_URL + "user"
assert kwargs["headers"] == {"Authorization": "auth_header"}
assert kwargs["json"] == {"config": {}}
assert kwargs["timeout"] == hub_model.TIMEOUT
@@ -555,7 +571,7 @@ def test_clear_user_configs_connection_error():
assert result is None
requests_put_mock.assert_called_once()
_, kwargs = requests_put_mock.call_args
assert kwargs["url"] == hub_model.BASE_URL + "user"
assert kwargs["url"] == hub_model.BackendEnvironment.BASE_URL + "user"
assert kwargs["headers"] == {"Authorization": "auth_header"}
assert kwargs["json"] == {"config": {}}
assert kwargs["timeout"] == hub_model.TIMEOUT
@@ -570,7 +586,7 @@ def test_clear_user_configs_exception():
assert result is None
requests_put_mock.assert_called_once()
_, kwargs = requests_put_mock.call_args
assert kwargs["url"] == hub_model.BASE_URL + "user"
assert kwargs["url"] == hub_model.BackendEnvironment.BASE_URL + "user"
assert kwargs["headers"] == {"Authorization": "auth_header"}
assert kwargs["json"] == {"config": {}}
assert kwargs["timeout"] == hub_model.TIMEOUT

View File

@@ -234,6 +234,7 @@ def test_download_routine(mocker, test_user, silent=False):
fields=["name", "script"],
page=1,
size=100,
base_url="https://payments.openbb.co/",
silent=silent,
)
@@ -272,6 +273,7 @@ def test_download_default_routine_exception(mocker, test_user, silent=False):
fields=["name", "script"],
page=1,
size=100,
base_url="https://payments.openbb.co/",
silent=silent,
)
@@ -310,6 +312,7 @@ def test_download_personal_routine_exception(mocker, test_user, silent=False):
fields=["name", "script"],
page=1,
size=100,
base_url="https://payments.openbb.co/",
silent=silent,
)
@@ -339,5 +342,6 @@ def test_download_default_and_personal_routine_exception(
fields=["name", "script"],
page=1,
size=100,
base_url="https://payments.openbb.co/",
silent=silent,
)

View File

@@ -75,7 +75,9 @@ def test_login_no_response(mocker):
assert session_model.login(TEST_SESSION) == session_model.LoginStatus.NO_RESPONSE
mock_fetch_user_configs.assert_called_once_with(TEST_SESSION)
mock_fetch_user_configs.assert_called_once_with(
TEST_SESSION, "https://payments.openbb.co/"
)
mock_apply_configs.assert_not_called()
mock_update_flair.assert_not_called()
mock_get_updated_hub_sources.assert_not_called()
@@ -96,7 +98,9 @@ def test_login_fail_response(mocker):
assert session_model.login(TEST_SESSION) == session_model.LoginStatus.FAILED
mock_fetch_user_configs.assert_called_once_with(TEST_SESSION)
mock_fetch_user_configs.assert_called_once_with(
TEST_SESSION, "https://payments.openbb.co/"
)
mock_apply_configs.assert_not_called()
mock_update_flair.assert_not_called()
mock_get_updated_hub_sources.assert_not_called()
@@ -119,7 +123,9 @@ def test_login_success_response(mocker):
assert session_model.login(TEST_SESSION) == session_model.LoginStatus.SUCCESS
mock_fetch_user_configs.assert_called_once_with(TEST_SESSION)
mock_fetch_user_configs.assert_called_once_with(
TEST_SESSION, "https://payments.openbb.co/"
)
mock_apply_configs.assert_called_once()
mock_update_flair.assert_called_once()
@@ -142,10 +148,13 @@ def test_logout_user(mocker, guest):
auth_header = "Bearer test_token"
token = "test_token"
base_url = "https://payments.openbb.co/"
session_model.logout(auth_header, token)
if not guest:
mock_delete_session.assert_called_once_with(auth_header, token)
mock_delete_session.assert_called_once_with(
auth_header, token, base_url=base_url
)
assert mock_remove.call_args_list[0] == mocker.call(SESSION_FILE_PATH)
assert mock_remove.call_args_list[1] == mocker.call(HIST_FILE_PATH)
assert mock_remove.call_args_list[2] == mocker.call(

View File

@@ -238,16 +238,6 @@ def test_call_func_expect_queue(expected_queue, queue, func):
@pytest.mark.parametrize(
"tested_func, other_args, mocked_func, called_args, called_kwargs",
[
(
"call_spacc",
["--limit=5", "--popular"],
"reddit_view.display_spac_community",
[],
dict(
limit=5,
popular=True,
),
),
(
"call_wsb",
["--limit=5", "--new"],
@@ -426,7 +416,6 @@ def test_call_func(
@pytest.mark.parametrize(
"func",
[
"call_spacc",
"call_wsb",
"call_popular",
"call_bullbear",

View File

@@ -6,7 +6,6 @@ Ticker: TSLA
snews stock price displayed over sentiment of news headlines [Finnhub]
wsb show what WSB gang is up to in subreddit wallstreetbets [Reddit]
popular show popular tickers [Reddit]
spacc show other users spacs announcements from subreddit SPACs [Reddit]
getdd gets due diligence from another user's post [Reddit]
redditsent searches reddit for ticker and finds reddit sentiment [Reddit]
trending trending stocks [Stocktwits]

View File

@@ -68,10 +68,10 @@ def no_suppress():
@pytest.mark.vcr(record_mode="none")
@pytest.mark.record_stdout
@pytest.mark.parametrize(
"debug, path",
[(True, None), (False, ["scripts/test_alt_covid.openbb"])],
"debug, dev, path",
[(True, False, None), (False, False, ["scripts/test_alt_covid.openbb"])],
)
def test_menu(mocker, debug, path):
def test_menu(mocker, debug, dev, path):
mocker.patch(target="terminal_controller.terminal")
mocker.patch(target="terminal_controller.suppress_stdout", side_effect=no_suppress)
terminal_controller.main(debug, path)
terminal_controller.main(debug, dev, path)

View File

@@ -3,7 +3,7 @@ title: v2.0.0
version: "2.0.0"
date: 2022-11-29
description: v2.0.0
sidebar_position: 104
sidebar_position: 105
---
## Major Release Notes

View File

@@ -3,7 +3,7 @@ title: v2.1.0
version: "2.1.0"
date: 2022-12-21
description: v2.1.0
sidebar_position: 103
sidebar_position: 104
---
## What's new

View File

@@ -3,7 +3,7 @@ title: v2.2.0
version: "2.2.0"
date: 2022-01-19
description: v2.2.0
sidebar_position: 102
sidebar_position: 103
---
## What's new

View File

@@ -3,7 +3,7 @@ title: v2.3.0
version: "2.3.0"
date: 2023-01-31
description: v2.3.0
sidebar_position: 101
sidebar_position: 102
---

View File

@@ -3,7 +3,7 @@ title: v2.3.1
version: "2.3.1"
date: 2023-02-01
description: v2.3.1
sidebar_position: 100
sidebar_position: 101
---
## Fixed

View File

@@ -3,7 +3,7 @@ title: v2.4.0
version: "2.4.0"
date: 2023-02-14
description: v2.4.0
sidebar_position: 99
sidebar_position: 100
---
## What's new

View File

@@ -3,7 +3,7 @@ title: v2.4.1
version: "2.4.1"
date: 2023-02-17
description: v2.4.1
sidebar_position: 98
sidebar_position: 99
---
## What's new

View File

@@ -3,7 +3,7 @@ title: v2.5.0
version: "2.5.0"
date: 2023-03-01
description: v2.5.0
sidebar_position: 97
sidebar_position: 98
---

View File

@@ -3,7 +3,7 @@ title: 2.5.1
version: "2.5.1"
date: 2023-03-01
description: Current - v2.5.1
sidebar_position: 96
sidebar_position: 97
---
## Fixed

View File

@@ -3,7 +3,7 @@ title: v3.0.0
version: "3.0.0"
date: 2023-04-25
description: v3.0.0
sidebar_position: 95
sidebar_position: 96
---
## What's new

View File

@@ -3,7 +3,7 @@ title: v3.0.1
version: "3.0.1"
date: 2023-05-15
description: v3.0.1
sidebar_position: 94
sidebar_position: 95
---
## Thank you and welcome to our new contributors 🔥

View File

@@ -3,7 +3,7 @@ title: v3.1.0
version: "3.1.0"
date: 2023-06-21
description: v3.1.0
sidebar_position: 93
sidebar_position: 94
---
## Thank you and welcome to our new contributors 🔥

View File

@@ -1,9 +1,9 @@
---
title: Current - v3.2.0
title: v3.2.0
version: "3.2.0"
date: 2023-07-21
description: Current - v3.2.0
sidebar_position: 92
sidebar_position: 93
---
## Thank you and welcome to our new contributors 🔥

View File

@@ -0,0 +1,54 @@
---
title: Current - v3.2.1
version: "3.2.1"
date: 2023-08-16
description: Current - v3.2.1
sidebar_position: 92
---
## What's new 🎉
We are super excited to launch community routines in the terminal!
This is accompanied by our usual bug fix and general enhancements.
## What's changed 🚀
* hotfix/simplify-regex (#5324) @DidierRLopes
* Fix routines creation from the Terminal to Hub (#5312) @DidierRLopes
* Fix regex (#5320) @DidierRLopes
* Creates a "--dev" flag that points to dev backend for testing with hub (#5319) @DidierRLopes
* small fixy (#5318) @DidierRLopes
* few more updates (#5317) @DidierRLopes
* --debug points to dev env (#5316) @DidierRLopes
* Silly (#5313) @DidierRLopes
* hotfix/ `/stocks/fa/pt` show all price targets on hover (#5305) @tehcoderer
* hotfix/options-hist: Fixes `options/hist` to display date column when raw is True (#5304) @deeleeramone
* Allow the exe command to run URLs (#5295) @colin99d
* Feature/news sentiment (#5274) @Sai-132
* hotfix/ conflict docstrings (#5293) @tehcoderer
* hotfix / forecast, qa/hist (#5290) @tehcoderer
* Fixed the get routines to meet the new backend standard (#5271) @colin99d
* Actually fix databento this time (#5287) @jmaslek
* add `--india` flag to `stocks/load` (#5281) @the-praxs
* Feature/options_views: Add a Variety of Views/Charts to Extend the OptionsChains Class (#5222) @deeleeramone
* hotfix/economy-controller-sort-filter: Fwd P/E regex fix (#5273) @deeleeramone
* Update all_api_keys.json (#5277) @DidierRLopes
* Move hub api keys ts (#5267) @montezdesousa
* Hotfix `economy/overview` rates displaying as dates (#5269) @tehcoderer
* Adds details/image for installing C++ Build Tools (#5268) @deeleeramone
* hotfix/gdp-units - better GDP units (#5262) @deeleeramone
* Hotfix/ locking when deploying streamlit app (#5261) @tehcoderer
* Fix issue where incorrect key for biztoc prints exception (#5257) @jmaslek
* Check token expiration before request (#5255) @montezdesousa
* hotfix/economy-report-duplicate-table: fixes duplicate tables. (#5252) @deeleeramone
* hotfix/docs-sdk-basics-headTitle: fix headTitle (#5253) @deeleeramone
* Fix/reports (#5251) @jmaslek
* Fix/reports (#5250) @jmaslek
* Release/3.2.0 (#5243) @jmaslek
We are proud of our community contributors and staunch supporters of open-source ecosystems.
Help us promote our community by tagging `@openbb_finance` on Twitter with a link to your pull request,
and join our Discord server to chat about your contribution! We want to hear about your experience!
### Links 🦋
[Website](https://openbb.co/), [Twitter](https://twitter.com/openbb_finance), [Linkedin](https://www.linkedin.com/company/openbb-finance), [Instagram](https://www.instagram.com/openbb.finance/), [Reddit](https://www.reddit.com/r/openbb/), [Discord](https://discord.com/invite/xPHTuHCmuV)

View File

@@ -37,7 +37,7 @@ Install the OpenBB Terminal on MacOS (Big Sur or later). There are two versions
<div style={{
height: 80
}}>
<InstallerButton href="https://github.com/OpenBB-finance/OpenBBTerminal/releases/download/v3.2.0/x86.64.MacOS.OpenBB.Terminal.v3.2.0.pkg" label="Mac Intel Installer" /> <InstallerButton href="https://github.com/OpenBB-finance/OpenBBTerminal/releases/download/v3.2.0/ARM64.MacOS.OpenBB.Terminal.v3.2.0.pkg" label="Mac M1 Installer" />
<InstallerButton href="https://github.com/OpenBB-finance/OpenBBTerminal/releases/download/v3.2.1/x86.64.MacOS.OpenBB.Terminal.v3.2.1.pkg" label="Mac Intel Installer" /> <InstallerButton href="https://github.com/OpenBB-finance/OpenBBTerminal/releases/download/v3.2.1/ARM64.MacOS.OpenBB.Terminal.v3.2.1.pkg" label="Mac M1 Installer" />
</div>
<details><summary>Minimum Requirements</summary>

View File

@@ -30,7 +30,7 @@ import InstallerButton from "@site/src/components/General/InstallerButton";
This section provides you with the installation file as well as the guide to install the OpenBB Terminal via Windows (10 or greater).
<InstallerButton type="windows" href="https://github.com/OpenBB-finance/OpenBBTerminal/releases/download/v3.2.0/Windows.OpenBB.Terminal.v3.2.0.exe" label="Windows Installer" />
<InstallerButton type="windows" href="https://github.com/OpenBB-finance/OpenBBTerminal/releases/download/v3.2.1/Windows.OpenBB.Terminal.v3.2.1.exe" label="Windows Installer" />
<details><summary>Minimum Requirements</summary>