diff --git a/openbb_terminal/core/log/generation/directories.py b/openbb_terminal/core/log/generation/directories.py index bf7de8b132d..a10c89bd67d 100644 --- a/openbb_terminal/core/log/generation/directories.py +++ b/openbb_terminal/core/log/generation/directories.py @@ -1,5 +1,4 @@ # IMPORTATION STANDARD -import os import uuid from pathlib import Path @@ -14,25 +13,23 @@ from openbb_terminal.core.config.paths import USER_DATA_DIRECTORY def get_log_dir() -> Path: """Retrieve application's log directory.""" - log_dir = USER_DATA_DIRECTORY.joinpath("logs") + log_dir = USER_DATA_DIRECTORY.joinpath("logs").absolute() - if not os.path.isdir(log_dir.absolute()): - os.mkdir(log_dir.absolute()) + if not log_dir.is_dir(): + log_dir.mkdir(parents=True, exist_ok=True) - log_id = log_dir.absolute().joinpath(".logid") + log_id = (log_dir / ".logid").absolute() - if not os.path.isfile(log_id.absolute()): + if not log_id.is_file(): logging_id = f"{uuid.uuid4()}" - with open(log_id.absolute(), "a") as a_file: - a_file.write(f"{logging_id}\n") + log_id.write_text(logging_id, encoding="utf-8") else: - with open(log_id.absolute()) as a_file: - logging_id = a_file.readline().rstrip() + logging_id = log_id.read_text(encoding="utf-8").rstrip() - uuid_log_dir = log_dir.absolute().joinpath(logging_id) + uuid_log_dir = (log_dir / logging_id).absolute() - if not os.path.isdir(uuid_log_dir.absolute()): - os.mkdir(uuid_log_dir.absolute()) + if not uuid_log_dir.is_dir(): + uuid_log_dir.mkdir(parents=True, exist_ok=True) return uuid_log_dir diff --git a/openbb_terminal/forex/forex_controller.py b/openbb_terminal/forex/forex_controller.py index 1ef383d19c7..1be022c09dd 100644 --- a/openbb_terminal/forex/forex_controller.py +++ b/openbb_terminal/forex/forex_controller.py @@ -37,7 +37,8 @@ logger = logging.getLogger(__name__) forex_data_path = os.path.join( os.path.dirname(__file__), os.path.join("data", "polygon_tickers.csv") ) -FX_TICKERS = pd.read_csv(forex_data_path).iloc[:, 0].to_list() +tickers = pd.read_csv(forex_data_path).iloc[:, 0].to_list() +FX_TICKERS = list(set(tickers + [t[-3:] + t[:3] for t in tickers if len(t) == 6])) class ForexController(BaseController): @@ -73,7 +74,6 @@ class ForexController(BaseController): if session and obbff.USE_PROMPT_TOOLKIT: choices: dict = self.choices_default - choices["load"].update({c: {} for c in FX_TICKERS}) self.completer = NestedCompleter.from_nested_dict(choices) diff --git a/openbb_terminal/miscellaneous/library/trail_map.csv b/openbb_terminal/miscellaneous/library/trail_map.csv index ceb9940b51f..6a026cdc6fe 100644 --- a/openbb_terminal/miscellaneous/library/trail_map.csv +++ b/openbb_terminal/miscellaneous/library/trail_map.csv @@ -478,6 +478,7 @@ stocks.options.dte,openbb_terminal.stocks.options.yfinance_model.get_dte, stocks.options.eodchain,openbb_terminal.stocks.options.intrinio_model.get_full_chain_eod, stocks.options.expirations,openbb_terminal.stocks.options.options_sdk_helper.get_option_expirations, stocks.options.generate_data,openbb_terminal.stocks.options.yfinance_model.generate_data, +stocks.options.greeks,openbb_terminal.stocks.options.options_sdk_helper.get_greeks, stocks.options.grhist,openbb_terminal.stocks.options.screen.syncretism_model.get_historical_greeks,openbb_terminal.stocks.options.screen.syncretism_view.view_historical_greeks stocks.options.hist,openbb_terminal.stocks.options.options_sdk_helper.hist, stocks.options.info,openbb_terminal.stocks.options.barchart_model.get_options_info,openbb_terminal.stocks.options.barchart_view.print_options_data diff --git a/openbb_terminal/sdk_core/controllers/stocks_sdk_controller.py b/openbb_terminal/sdk_core/controllers/stocks_sdk_controller.py index 26679114052..794b48336eb 100644 --- a/openbb_terminal/sdk_core/controllers/stocks_sdk_controller.py +++ b/openbb_terminal/sdk_core/controllers/stocks_sdk_controller.py @@ -271,6 +271,7 @@ class StocksController(model.StocksRoot): `eodchain`: Get full EOD option date across all expirations\n `expirations`: Get Option Chain Expirations\n `generate_data`: Gets x values, and y values before and after premiums\n + `greeks`: Gets the greeks for a given option\n `grhist`: Get historical option greeks\n `grhist_chart`: Plots historical greeks for a given option. [Source: Syncretism]\n `hist`: Get historical option pricing.\n diff --git a/openbb_terminal/sdk_core/models/stocks_sdk_model.py b/openbb_terminal/sdk_core/models/stocks_sdk_model.py index 4399d7b6a16..0d8309323d1 100644 --- a/openbb_terminal/sdk_core/models/stocks_sdk_model.py +++ b/openbb_terminal/sdk_core/models/stocks_sdk_model.py @@ -491,6 +491,7 @@ class StocksOptions(Category): self.eodchain = lib.stocks_options_intrinio_model.get_full_chain_eod self.expirations = lib.stocks_options_sdk_helper.get_option_expirations self.generate_data = lib.stocks_options_yfinance_model.generate_data + self.greeks = lib.stocks_options_sdk_helper.get_greeks self.grhist = lib.stocks_options_screen_syncretism_model.get_historical_greeks self.grhist_chart = ( lib.stocks_options_screen_syncretism_view.view_historical_greeks diff --git a/openbb_terminal/stocks/options/options_sdk_helper.py b/openbb_terminal/stocks/options/options_sdk_helper.py index 39715225cb8..7744b31e2e5 100644 --- a/openbb_terminal/stocks/options/options_sdk_helper.py +++ b/openbb_terminal/stocks/options/options_sdk_helper.py @@ -1,6 +1,7 @@ """Options Functions For OpenBB SDK""" import logging +from datetime import datetime, timedelta from typing import Optional, Union import numpy as np @@ -8,6 +9,7 @@ import pandas as pd from scipy.optimize import minimize from openbb_terminal.decorators import log_start_end +from openbb_terminal.helper_funcs import get_rf from openbb_terminal.rich_config import console from openbb_terminal.stocks.options import ( chartexchange_model, @@ -17,6 +19,7 @@ from openbb_terminal.stocks.options import ( tradier_model, yfinance_model, ) +from openbb_terminal.stocks.options.op_helpers import Option logger = logging.getLogger(__name__) @@ -235,3 +238,97 @@ def get_delta_neutral(symbol: str, date: str, x0: Optional[float] = None) -> flo "Error getting delta neutral price for %s on %s: error:%s", symbol, date, e ) return np.nan + + +def get_greeks( + current_price: float, + chain: pd.DataFrame, + expire: str, + div_cont: float = 0, + rf: Optional[float] = None, +) -> pd.DataFrame: + """ + Gets the greeks for a given option + + Parameters + ---------- + current_price: float + The current price of the underlying + chain: pd.DataFrame + The dataframe with option chains + div_cont: float + The dividend continuous rate + expire: str + The date of expiration + rf: float + The risk-free rate + + Returns + ------- + pd.DataFrame + Dataframe with calculated option greeks + + Examples + -------- + >>> from openbb_terminal.sdk import openbb + >>> aapl_chain = openbb.stocks.options.chains("AAPL", source="Tradier") + >>> aapl_last_price = openbb.stocks.options.last_price("AAPL") + >>> greeks = openbb.stocks.options.greeks(aapl_last_price, aapl_chain, aapl_chain.iloc[0, 2]) + """ + + chain = chain.rename(columns={"iv": "impliedVolatility"}) + chain_columns = chain.columns.tolist() + if not all( + col in chain_columns for col in ["strike", "impliedVolatility", "optionType"] + ): + if "delta" not in chain_columns: + console.print( + "[red]It's not possible to calculate the greeks without the following " + "columns: `strike`, `impliedVolatility`, `optionType`.\n[/red]" + ) + return pd.DataFrame() + + risk_free = rf if rf is not None else get_rf() + expire_dt = datetime.strptime(expire, "%Y-%m-%d") + dif = (expire_dt - datetime.now() + timedelta(hours=16)).total_seconds() / ( + 60 * 60 * 24 + ) + strikes = [] + for _, row in chain.iterrows(): + vol = row["impliedVolatility"] + opt_type = 1 if row["optionType"] == "call" else -1 + opt = Option( + current_price, row["strike"], risk_free, div_cont, dif, vol, opt_type + ) + tmp = [ + opt.Delta(), + opt.Gamma(), + opt.Vega(), + opt.Theta(), + opt.Rho(), + opt.Phi(), + opt.Charm(), + opt.Vanna(0.01), + opt.Vomma(0.01), + ] + result = [row[col] for col in row.index.tolist()] + result += tmp + + strikes.append(result) + + greek_columns = [ + "Delta", + "Gamma", + "Vega", + "Theta", + "Rho", + "Phi", + "Charm", + "Vanna", + "Vomma", + ] + columns = chain_columns + greek_columns + + df = pd.DataFrame(strikes, columns=columns) + + return df