mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-27 04:31:13 +00:00
Hotfix/options plot-smile (#4613)
* init * Update style.css * oops was stashed * Update popups.js * bump plotly.js version * fix disappearing modebar
This commit is contained in:
@@ -48,7 +48,7 @@ else:
|
||||
JUPYTER_NOTEBOOK = True
|
||||
|
||||
PLOTS_CORE_PATH = Path(__file__).parent.resolve()
|
||||
PLOTLYJS_PATH = PLOTS_CORE_PATH / "assets" / "plotly-2.18.2.min.js"
|
||||
PLOTLYJS_PATH = PLOTS_CORE_PATH / "assets" / "plotly-2.20.0.min.js"
|
||||
BACKEND = None
|
||||
|
||||
|
||||
@@ -93,7 +93,9 @@ class Backend(PyWry):
|
||||
try:
|
||||
with open(PLOTS_CORE_PATH / "plotly.html", encoding="utf-8") as file: # type: ignore
|
||||
html = file.read()
|
||||
html = html.replace("{{MAIN_PATH}}", str(PLOTS_CORE_PATH.as_uri()))
|
||||
html = html.replace(
|
||||
"{{MAIN_PATH}}", str(PLOTS_CORE_PATH.as_uri())
|
||||
).replace("{{PLOTLYJS_PATH}}", str(PLOTLYJS_PATH.as_uri()))
|
||||
|
||||
# We create a temporary file to inject the path to the script tag
|
||||
# This is so we don't have to modify the original file
|
||||
@@ -374,7 +376,9 @@ async def download_plotly_js():
|
||||
try:
|
||||
# we use aiohttp to download plotly.js
|
||||
# this is so we don't have to block the main thread
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with aiohttp.ClientSession(
|
||||
connector=aiohttp.TCPConnector(verify_ssl=False)
|
||||
) as session:
|
||||
async with session.get(f"https://cdn.plot.ly/{js_filename}") as resp:
|
||||
with open(str(PLOTLYJS_PATH), "wb") as f:
|
||||
while True:
|
||||
|
||||
15
openbb_terminal/core/plots/plotly.html
vendored
15
openbb_terminal/core/plots/plotly.html
vendored
@@ -12,7 +12,7 @@
|
||||
<script src="/{{MAIN_PATH}}/web/popups.js" type="text/javascript"></script>
|
||||
<script src="/{{MAIN_PATH}}/web/main.js" type="text/javascript"></script>
|
||||
<script
|
||||
src="/{{MAIN_PATH}}/assets/plotly-2.18.2.min.js"
|
||||
src="/{{PLOTLYJS_PATH}}"
|
||||
type="text/javascript"
|
||||
></script>
|
||||
<script
|
||||
@@ -25,11 +25,11 @@
|
||||
|
||||
<body>
|
||||
<!-- Plotly -->
|
||||
<div id="all-your-base">
|
||||
<div id="openbb_container">
|
||||
<div
|
||||
id="openbb_header"
|
||||
class="_header flex items-center justify-between gap-4 py-2 px-4 text-center"
|
||||
style="z-index: 99999999999; width: 100%; top: 0;"
|
||||
style="z-index: 99999999999; width: 100%; top: 0; max-height: 60px;"
|
||||
>
|
||||
<div class="w-1/3">
|
||||
<svg
|
||||
@@ -52,10 +52,15 @@
|
||||
id="openbb_chart"
|
||||
class="plotly-graph-div"
|
||||
></div>
|
||||
<div
|
||||
id="openbb_footer"
|
||||
class="_footer flex items-center justify-between gap-4 py-2 px-4 text-center"
|
||||
style="z-index: 99999999999; width: 100%; bottom: 0; max-height: 60px;"
|
||||
>......................</div>
|
||||
</div>
|
||||
|
||||
<div id="saving" class="saving">
|
||||
Saving Image <div id="loader" class="loader"></div>
|
||||
<div id="loading" class="saving">
|
||||
<div id="loading_text" class="loading_text"></div><div id="loader" class="loader"></div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@@ -1525,7 +1525,7 @@ class OpenBBFigure(go.Figure):
|
||||
return
|
||||
|
||||
margin_add = (
|
||||
dict(l=80, r=60, b=85, t=60, pad=0)
|
||||
dict(l=80, r=60, b=85, t=40, pad=0)
|
||||
if not self._has_secondary_y
|
||||
else dict(l=60, r=50, b=85, t=40, pad=0)
|
||||
)
|
||||
|
||||
2
openbb_terminal/core/plots/web/bar_menus.js
vendored
2
openbb_terminal/core/plots/web/bar_menus.js
vendored
@@ -237,7 +237,7 @@ function downloadImage() {
|
||||
const loader = document.getElementById("loader");
|
||||
loader.classList.add("show");
|
||||
domtoimage
|
||||
.toPng(document.getElementById("all-your-base"))
|
||||
.toPng(document.getElementById("openbb_container"))
|
||||
.then(function (dataUrl) {
|
||||
let img = new Image(
|
||||
globals.CHART_DIV.clientWidth,
|
||||
|
||||
26
openbb_terminal/core/plots/web/css/style.css
vendored
26
openbb_terminal/core/plots/web/css/style.css
vendored
@@ -46,7 +46,8 @@ body {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.all-your-base {
|
||||
.openbb_container {
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #000000;
|
||||
@@ -74,7 +75,6 @@ body {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
|
||||
#changecolor {
|
||||
position: absolute;
|
||||
z-index: 9;
|
||||
@@ -196,13 +196,13 @@ body {
|
||||
|
||||
.modebar {
|
||||
z-index: 5000 !important;
|
||||
position: absolute !important;
|
||||
bottom: 0px !important;
|
||||
position: fixed !important;
|
||||
bottom: 10px !important;
|
||||
top: auto !important;
|
||||
left: 50% !important;
|
||||
right: auto !important;
|
||||
align-items: center;
|
||||
transform: translateX(-50%) translateY(80%) !important;
|
||||
transform: translate(-50%, 10%) !important;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
@@ -212,7 +212,7 @@ body {
|
||||
}
|
||||
|
||||
.modebar {
|
||||
bottom: 60px !important;
|
||||
bottom: 20px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -597,10 +597,6 @@ button {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
animation: spin 1.5s linear infinite;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.loader.show {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@@ -1315,3 +1311,13 @@ video {
|
||||
#06101a 82%,
|
||||
#060709);
|
||||
}
|
||||
|
||||
|
||||
@media screen and (max-height: 1000px) and (max-width: 1100px) {
|
||||
._header strong {
|
||||
font-size: 1.1rem !important;
|
||||
}
|
||||
._header p {
|
||||
font-size: 1.0rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
67
openbb_terminal/core/plots/web/main.js
vendored
67
openbb_terminal/core/plots/web/main.js
vendored
@@ -6,6 +6,30 @@ let globals = {
|
||||
cmd_src_idx: null,
|
||||
};
|
||||
|
||||
function loadingOverlay(message) {
|
||||
const loading = document.getElementById("loading");
|
||||
const loading_text = document.getElementById("loading_text");
|
||||
|
||||
loading_text.innerHTML = message;
|
||||
loading.classList.add("show");
|
||||
|
||||
let is_loaded = setInterval(function () {
|
||||
if (loading.classList.contains("show")) {
|
||||
clearInterval(is_loaded);
|
||||
}
|
||||
}, 10);
|
||||
}
|
||||
|
||||
const non_blocking = (func, delay) => {
|
||||
let timeout;
|
||||
return function () {
|
||||
const context = this;
|
||||
const args = arguments;
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => func.apply(context, args), delay);
|
||||
};
|
||||
};
|
||||
|
||||
function OpenBBMain(plotly_figure, chartdiv, csvdiv, textdiv, titlediv) {
|
||||
// Main function that plots the graphs and initializes the bar menus
|
||||
globals.CHART_DIV = chartdiv;
|
||||
@@ -16,15 +40,22 @@ function OpenBBMain(plotly_figure, chartdiv, csvdiv, textdiv, titlediv) {
|
||||
console.log("plotly_figure", plotly_figure);
|
||||
let graphs = plotly_figure;
|
||||
|
||||
const loader = document.getElementById("loader");
|
||||
const saving = document.getElementById("saving");
|
||||
const loading = document.getElementById("loading");
|
||||
|
||||
// We add the event listeners for csv file/type changes
|
||||
globals.CSV_DIV.querySelector("#csv_file").addEventListener(
|
||||
"change",
|
||||
function () {
|
||||
console.log("file changed");
|
||||
checkFile(globals.CSV_DIV);
|
||||
loadingOverlay("Loading CSV");
|
||||
setTimeout(function () {
|
||||
non_blocking(function () {
|
||||
checkFile(globals.CSV_DIV);
|
||||
}, 2)();
|
||||
setTimeout(function () {
|
||||
loading.classList.remove("show");
|
||||
}, 200);
|
||||
}, 1000);
|
||||
}
|
||||
);
|
||||
globals.CSV_DIV.querySelector("#csv_trace_type").addEventListener(
|
||||
@@ -34,17 +65,8 @@ function OpenBBMain(plotly_figure, chartdiv, csvdiv, textdiv, titlediv) {
|
||||
checkFile(globals.CSV_DIV, true);
|
||||
}
|
||||
);
|
||||
globals.filename = openbbFilename(graphs);
|
||||
|
||||
const non_blocking = (func, delay) => {
|
||||
let timeout;
|
||||
return function () {
|
||||
const context = this;
|
||||
const args = arguments;
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => func.apply(context, args), delay);
|
||||
};
|
||||
};
|
||||
globals.filename = openbbFilename(graphs);
|
||||
|
||||
// Sets the config with the custom buttons
|
||||
CONFIG = {
|
||||
@@ -59,19 +81,12 @@ function OpenBBMain(plotly_figure, chartdiv, csvdiv, textdiv, titlediv) {
|
||||
name: "Download Data (Ctrl+S)",
|
||||
icon: Plotly.Icons.disk,
|
||||
click: function (gd) {
|
||||
let saved_csv = document.createElement("div");
|
||||
saved_csv.innerHTML = "Saved CSV";
|
||||
saved_csv.classList.add("saving");
|
||||
saved_csv.classList.add("show");
|
||||
document.body.appendChild(saved_csv);
|
||||
loadingOverlay("Saving CSV");
|
||||
setTimeout(function () {
|
||||
downloadData(gd);
|
||||
}, 500);
|
||||
setTimeout(function () {
|
||||
saved_csv.classList.remove("show");
|
||||
setTimeout(function () {
|
||||
document.body.removeChild(saved_csv);
|
||||
}, 500);
|
||||
loading.classList.remove("show");
|
||||
}, 1000);
|
||||
},
|
||||
},
|
||||
@@ -86,15 +101,13 @@ function OpenBBMain(plotly_figure, chartdiv, csvdiv, textdiv, titlediv) {
|
||||
name: "Download Plot",
|
||||
icon: Plotly.Icons.camera,
|
||||
click: function () {
|
||||
saving.classList.add("show");
|
||||
loader.classList.add("show");
|
||||
loadingOverlay("Saving Image");
|
||||
hideModebar();
|
||||
non_blocking(function () {
|
||||
downloadImage();
|
||||
setTimeout(function () {
|
||||
setTimeout(function () {
|
||||
saving.classList.remove("show");
|
||||
loader.classList.remove("show");
|
||||
loading.classList.remove("show");
|
||||
hideModebar();
|
||||
}, 50);
|
||||
}, 25);
|
||||
@@ -259,10 +272,12 @@ function OpenBBMain(plotly_figure, chartdiv, csvdiv, textdiv, titlediv) {
|
||||
// We check for the dark mode
|
||||
if (graphs.layout.template.layout.mapbox.style == "dark") {
|
||||
document.body.style.backgroundColor = "#000000";
|
||||
document.getElementById("openbb_footer").style.color = "#000000";
|
||||
graphs.layout.template.layout.paper_bgcolor = "#000000";
|
||||
graphs.layout.template.layout.plot_bgcolor = "#000000";
|
||||
} else {
|
||||
document.body.style.backgroundColor = "#FFFFFF";
|
||||
document.getElementById("openbb_footer").style.color = "#FFFFFF";
|
||||
}
|
||||
|
||||
// We set the plot config and plot the chart
|
||||
|
||||
2
openbb_terminal/core/plots/web/popups.js
vendored
2
openbb_terminal/core/plots/web/popups.js
vendored
@@ -384,6 +384,7 @@ function on_submit(popup_id, on_annotation = null) {
|
||||
yaxis = main_trace.yaxis.replace("yaxis", "y");
|
||||
yaxis_id = main_trace.yaxis;
|
||||
}
|
||||
gd.layout.showlegend = true;
|
||||
|
||||
if (popup_data.trace_type == "candlestick") {
|
||||
trace = {
|
||||
@@ -410,7 +411,6 @@ function on_submit(popup_id, on_annotation = null) {
|
||||
yaxis: yaxis,
|
||||
};
|
||||
} else {
|
||||
gd.layout.showlegend = true;
|
||||
|
||||
orginal_data = data;
|
||||
let non_null = data.findIndex(
|
||||
|
||||
@@ -10,7 +10,7 @@ import numpy as np
|
||||
from scipy.spatial import Delaunay
|
||||
from scipy.stats import binom
|
||||
|
||||
from openbb_terminal import OpenBBFigure
|
||||
from openbb_terminal import OpenBBFigure, theme
|
||||
from openbb_terminal.core.plots.config.openbb_styles import (
|
||||
PLT_3DMESH_COLORSCALE,
|
||||
PLT_3DMESH_HOVERLABEL,
|
||||
@@ -95,6 +95,7 @@ def plot_plot(
|
||||
external_axes : bool, optional
|
||||
Whether to return the figure object or not, by default False
|
||||
"""
|
||||
is_smile = custom == "smile"
|
||||
convert = {
|
||||
"ltd": "lastTradeDate",
|
||||
"s": "strike",
|
||||
@@ -107,7 +108,8 @@ def plot_plot(
|
||||
"oi": "openInterest",
|
||||
"iv": "impliedVolatility",
|
||||
}
|
||||
if custom == "smile":
|
||||
|
||||
if is_smile:
|
||||
x = "strike"
|
||||
y = "impliedVolatility"
|
||||
else:
|
||||
@@ -130,20 +132,42 @@ def plot_plot(
|
||||
f"[red]'{y}' is not a valid option. Defaulting to `impliedVolatility`.[/red]\n"
|
||||
)
|
||||
|
||||
varis = op_helpers.opt_chain_cols
|
||||
chain = yfinance_model.get_option_chain(symbol, expiry)
|
||||
values = chain.puts if put else chain.calls
|
||||
|
||||
x_data = values[x]
|
||||
y_data = values[y]
|
||||
fig = OpenBBFigure()
|
||||
if not is_smile:
|
||||
varis = op_helpers.opt_chain_cols
|
||||
values = chain.puts if put else chain.calls
|
||||
|
||||
option = "puts" if put else "calls"
|
||||
fig = OpenBBFigure(
|
||||
title=f"{varis[y]['label']} vs. {varis[x]['label']} for {symbol} {option} on {expiry}",
|
||||
xaxis_title=varis[x]["label"],
|
||||
yaxis_title=varis[y]["label"],
|
||||
)
|
||||
fig.add_scatter(x=x_data, y=y_data, mode="lines+markers")
|
||||
x_data = values[x]
|
||||
y_data = values[y]
|
||||
|
||||
option = "puts" if put else "calls"
|
||||
title = f"{varis[y]['label']} vs. {varis[x]['label']} for {symbol} {option} on {expiry}"
|
||||
xaxis_title = varis[x]["label"]
|
||||
yaxis_title = varis[y]["label"]
|
||||
|
||||
fig.add_scatter(x=x_data, y=y_data, mode="lines+markers")
|
||||
else:
|
||||
calls = chain.calls[chain.calls["impliedVolatility"] > 0.00002].dropna()
|
||||
puts = chain.puts[chain.puts["impliedVolatility"] > 0.00002].dropna()
|
||||
|
||||
for data, name in zip([calls, puts], ["Calls", "Puts"]):
|
||||
fig.add_scatter(
|
||||
x=data["strike"],
|
||||
y=data["impliedVolatility"],
|
||||
mode="lines+markers",
|
||||
name=name,
|
||||
line_color=theme.up_color if name == "Calls" else theme.down_color,
|
||||
)
|
||||
|
||||
title = f"Implied Volatility Smile for {symbol} on {expiry}"
|
||||
xaxis_title = "Strike"
|
||||
yaxis_title = "Implied Volatility"
|
||||
|
||||
fig.set_title(title)
|
||||
fig.set_xaxis_title(xaxis_title)
|
||||
fig.set_yaxis_title(yaxis_title)
|
||||
|
||||
export_data(
|
||||
export,
|
||||
|
||||
Reference in New Issue
Block a user