CatPtain commited on
Commit
ccefd0b
·
verified ·
1 Parent(s): b93364a

Upload 100 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +2 -0
  2. examples/BacktestingMomentumTrading.ipynb +0 -0
  3. examples/COMMUNITY_EXAMPLE_TEMPLATE.ipynb +71 -0
  4. examples/EthereumTrendAnalysis.ipynb +0 -0
  5. examples/README.md +95 -0
  6. examples/content.json +57 -0
  7. examples/copperToGoldRatio.ipynb +0 -0
  8. examples/currencyExchangeRateForecasting.ipynb +0 -0
  9. examples/financialStatements.ipynb +1244 -0
  10. examples/financialStatements.webp +0 -0
  11. examples/findSymbols.ipynb +0 -0
  12. examples/findSymbols.webp +0 -0
  13. examples/googleColab.ipynb +0 -0
  14. examples/googleColab.webp +0 -0
  15. examples/impliedEarningsMove.ipynb +691 -0
  16. examples/impliedEarningsMove.webp +0 -0
  17. examples/loadHistoricalPriceData.ipynb +2514 -0
  18. examples/mAndAImpact.ipynb +0 -0
  19. examples/openbb-apachebeam/README.md +12 -0
  20. examples/openbb-apachebeam/requirements.txt +2 -0
  21. examples/openbb-apachebeam/tests/__init__.py +0 -0
  22. examples/openbb-apachebeam/tests/test_obb_pipeline.py +48 -0
  23. examples/openbbPlatformAsLLMTools.ipynb +0 -0
  24. examples/platform_standardization.ipynb +761 -0
  25. examples/portfolioOptimizationUsingModernPortfolioTheory.ipynb +0 -0
  26. examples/riskReturnAnalysis.ipynb +0 -0
  27. examples/sectorRotationStrategy.ipynb +0 -0
  28. examples/streamlit/news.py +444 -0
  29. examples/streamlit/requirements.txt +3 -0
  30. examples/streamlit_news.webp +0 -0
  31. examples/usdLiquidityIndex.ipynb +0 -0
  32. frontend-components/fonts/FiraCode-Regular.ttf +3 -0
  33. frontend-components/fonts/FiraCode-VF.ttf +3 -0
  34. frontend-components/plotly/.gitignore +24 -0
  35. frontend-components/plotly/README.md +70 -0
  36. frontend-components/plotly/index.html +40 -0
  37. frontend-components/plotly/package-lock.json +0 -0
  38. frontend-components/plotly/package.json +42 -0
  39. frontend-components/plotly/postcss.config.cjs +6 -0
  40. frontend-components/plotly/src/App.tsx +142 -0
  41. frontend-components/plotly/src/components/AutoScaling.tsx +232 -0
  42. frontend-components/plotly/src/components/ChangeColor.tsx +95 -0
  43. frontend-components/plotly/src/components/Chart.tsx +902 -0
  44. frontend-components/plotly/src/components/Config.tsx +800 -0
  45. frontend-components/plotly/src/components/Dialogs/AlertDialog.tsx +42 -0
  46. frontend-components/plotly/src/components/Dialogs/CommonDialog.tsx +43 -0
  47. frontend-components/plotly/src/components/Dialogs/OverlayChartDialog.tsx +651 -0
  48. frontend-components/plotly/src/components/Dialogs/TextChartDialog.tsx +315 -0
  49. frontend-components/plotly/src/components/Dialogs/TitleChartDialog.tsx +157 -0
  50. frontend-components/plotly/src/components/Icons/Close.tsx +28 -0
.gitattributes CHANGED
@@ -36,3 +36,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
36
  build/conda/installer/assets/dmg_volume.icns filter=lfs diff=lfs merge=lfs -text
37
  build/conda/installer/assets/Installer_vertical2.bmp filter=lfs diff=lfs merge=lfs -text
38
  cli/openbb_cli/assets/styles/default/Consolas.ttf filter=lfs diff=lfs merge=lfs -text
 
 
 
36
  build/conda/installer/assets/dmg_volume.icns filter=lfs diff=lfs merge=lfs -text
37
  build/conda/installer/assets/Installer_vertical2.bmp filter=lfs diff=lfs merge=lfs -text
38
  cli/openbb_cli/assets/styles/default/Consolas.ttf filter=lfs diff=lfs merge=lfs -text
39
+ frontend-components/fonts/FiraCode-Regular.ttf filter=lfs diff=lfs merge=lfs -text
40
+ frontend-components/fonts/FiraCode-VF.ttf filter=lfs diff=lfs merge=lfs -text
examples/BacktestingMomentumTrading.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
examples/COMMUNITY_EXAMPLE_TEMPLATE.ipynb ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# Instructions for Contributors\n",
8
+ "\n",
9
+ "Welcome to this example notebook for OpenBB! Please follow the steps below:\n",
10
+ "\n",
11
+ "1. **Fill in the details**: Customize the second cell with the name of the notebook, your GitHub profile link, and a brief description of what your notebook demonstrates.\n",
12
+ "2. **Add Your Code**: Make sure to include clean and commented code sections throughout the notebook.\n",
13
+ "3. **Test Before Submitting**: Run all cells to ensure the notebook functions as expected.\n",
14
+ "4. **Keep it Simple and Clear**: Make your explanations and code as clear as possible for others to follow.\n",
15
+ "5. **Run in Colab Button**: Ensure the \"Run in Colab\" button links properly to the notebook. You can test it by clicking the button and verifying it loads your notebook.\n",
16
+ "\n",
17
+ "Please refer to the documentation at [OpenBB Documentation](https://docs.openbb.co/) for additional guidance.\n",
18
+ "\n",
19
+ "Remove this cell before submitting your notebook."
20
+ ]
21
+ },
22
+ {
23
+ "cell_type": "markdown",
24
+ "metadata": {},
25
+ "source": [
26
+ "## [Notebook Name]\n",
27
+ "\n",
28
+ "#### Description\n",
29
+ "[Briefly describe what this notebook demonstrates, e.g., \"This notebook demonstrates how to backtest a momentum trading strategy using OpenBB's historical data.\"]\n",
30
+ "\n",
31
+ "#### Author\n",
32
+ "[Your Name](https://github.com/[YourGitHubUsername])\n",
33
+ "\n",
34
+ "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/OpenBB-Finance/OpenBB/blob/develop/examples/[Notebook_Name].ipynb)"
35
+ ]
36
+ },
37
+ {
38
+ "cell_type": "markdown",
39
+ "metadata": {},
40
+ "source": [
41
+ "If you are running this notebook in Colab, you can run the following command to install the OpenBB Platform:\n",
42
+ "\n",
43
+ "```python\n",
44
+ "!pip install openbb\n",
45
+ "```\n"
46
+ ]
47
+ },
48
+ {
49
+ "cell_type": "code",
50
+ "execution_count": null,
51
+ "metadata": {},
52
+ "outputs": [],
53
+ "source": [
54
+ "from openbb import obb"
55
+ ]
56
+ }
57
+ ],
58
+ "metadata": {
59
+ "kernelspec": {
60
+ "display_name": "obb",
61
+ "language": "python",
62
+ "name": "python3"
63
+ },
64
+ "language_info": {
65
+ "name": "python",
66
+ "version": "3.9.19"
67
+ }
68
+ },
69
+ "nbformat": 4,
70
+ "nbformat_minor": 2
71
+ }
examples/EthereumTrendAnalysis.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
examples/README.md ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Jupyter Notebook Examples Using the OpenBB Platform
2
+
3
+ This folder is a collection of example notebooks that demonstrate some of the ways to get started with using the OpenBB Platform. To run them, ensure that the active kernel selected is the same Python virtual environment where OpenBB was installed.
4
+
5
+ ## Table of Contents
6
+
7
+ ### googleColab
8
+
9
+ This notebook installs the OpenBB Platform in a Google Colab environment with examples for:
10
+
11
+ - Logging into OpenBB Hub
12
+ - Setting the output preference
13
+ - Fetching options and company fundamentals data
14
+ - Creating bar chart visualizations
15
+
16
+ ### findSymbols
17
+
18
+ This notebook provides an introduction to discovering, finding, and searching ticker symbols.
19
+
20
+ - Search
21
+ - Find company and institutional filings
22
+ - Screen stocks by region and metrics
23
+
24
+ ### loadHistoricalPriceData
25
+
26
+ This notebook walks through collecting historical price data, at different intervals, using a variety of sources.
27
+
28
+ - Loading data with different intervals, and changing sources
29
+ - A brief explanation of ticker symbology
30
+ - Resampling a time series index
31
+ - Some differences between providers, and comparing outputs
32
+
33
+ ### financialStatements
34
+
35
+ This set of examples introduces financial statements in the OpenBB Platform and compares the free cash flow yields of large-cap retail industry companies.
36
+
37
+ - Financial statements
38
+ - What to expect with data from different sources
39
+ - Financial attributes
40
+ - Ratios and other metrics
41
+
42
+ ### copperToGoldRatio
43
+
44
+ This notebook explains how to calculate and plot the Copper-to-Gold ratio.
45
+
46
+ - Loading historical front-month futures prices.
47
+ - Getting the historical series from FRED for the 10-year constant maturity US treasury bill.
48
+ - Performing basic DataFrame operations.
49
+ - Creating charts with Plotly Graph Objects.
50
+
51
+ ### openbbPlatformAsLLMTools
52
+
53
+ This notebook shows you how you can use OpenbB Platform as functions in an LLM by leveraging function calling.
54
+
55
+ - Create an LLM tool from an OpenBB Platform function
56
+ - Convert all OpenBB Platform functions to LLM tools
57
+ - Build a basic Langchain agent that can utilize function calling
58
+ - Run the agent
59
+
60
+ ### usdLiquidityIndex
61
+
62
+ This notebook demonstrates how to query the Federal Reserve Economic Database and recreate the USD Liquidity Index.
63
+
64
+ - Search FRED for series IDs.
65
+ - Load multiple series as a single call.
66
+ - Unpacking the data response from the FRED query.
67
+ - Perform arithmetic operations on a DataFrame.
68
+ - Normalization methods for a series or DataFrame.
69
+ - Simple processes for creating charts.
70
+
71
+ ### impliedEarningsMove
72
+
73
+ This notebook demonstrates how to calculate the implied earnings move using options prices from free sources.
74
+
75
+ - Get upcoming earnings calendar.
76
+ - Fetch options chains data.
77
+ - Get the last price of the underlying stock.
78
+ - Find the nearest call and put strikes to the last price of the stock.
79
+ - Calculate the implied daily move using the price of a straddle.
80
+
81
+ ### streamlit/news
82
+
83
+ This is an example Streamlit dashboard for news headlines with data from Biztoc, Benzinga, FMP, Intrinio, and Tiingo.
84
+
85
+ :::warning
86
+ At least one API key is required. You can get a free Biztoc API key [here](https://rapidapi.com/thma/api/biztoc)
87
+ :::
88
+
89
+ To run, copy the file to your system, open a terminal, navigate to where the file is, and with your `obb` Python environment active, enter:
90
+
91
+ ```
92
+ pip install streamlit
93
+ pip install openbb-biztoc
94
+ streamlit run news.py
95
+ ```
examples/content.json ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "title": "Install in Google Colab",
4
+ "url": "https://github.com/OpenBB-finance/OpenBB/blob/develop/examples/googleColab.ipynb",
5
+ "img":"https://raw.githubusercontent.com/OpenBB-finance/OpenBBTerminal/develop/examples/googleColab.webp",
6
+ "description":
7
+ "Install the OpenBB Platform in Google Colab and get started pulling data and creating visualizations."
8
+ },
9
+ {
10
+ "title": "Find Symbols",
11
+ "url": "https://github.com/OpenBB-finance/OpenBB/blob/develop/examples/findSymbols.ipynb",
12
+ "img": "https://raw.githubusercontent.com/OpenBB-finance/OpenBBTerminal/develop/examples/findSymbols.webp",
13
+ "description":
14
+ "An introduction to discovering, finding, screening, and searching symbols using different sources."
15
+ },
16
+ {
17
+ "title": "Load Historical Price Data",
18
+ "url": "https://github.com/OpenBB-finance/OpenBB/blob/develop/examples/loadHistoricalPriceData.ipynb",
19
+ "img": "https://my.openbb.co/assets/images/sdk/examples/loadHistoricalPriceData.webp",
20
+ "description":
21
+ "Loading data with different intervals and sources, ticker symbology, load data from other asset classes, load multiple tickers in one go, draw lines on plotly."
22
+ },
23
+ {
24
+ "title": "Copper To Gold Ratio",
25
+ "url": "https://github.com/OpenBB-finance/OpenBB/blob/develop/examples/copperToGoldRatio.ipynb",
26
+ "img": "https://my.openbb.co/assets/images/sdk/examples/copperToGoldRatio.webp",
27
+ "description":
28
+ "Calculate copper to gold ratio, load front-month future prices, 10-year constant maturity vs treasury bill, basic dataframe operations, plotting on 2 y-axis."
29
+ },
30
+ {
31
+ "title": "USD Liquidity Index",
32
+ "url": "https://github.com/OpenBB-finance/OpenBB/blob/develop/examples/usdLiquidityIndex.ipynb",
33
+ "img": "https://my.openbb.co/assets/images/sdk/examples/usdLiquidityIndex.webp",
34
+ "description":
35
+ "Query the Federal Reserve Economic Database and recreate the USD Liquidity Index, load multiple data series, basic operations on a dataframe, normalization methods, and creating custom chart."
36
+ },
37
+ {
38
+ "title": "Financial Statements",
39
+ "url": "https://github.com/OpenBB-finance/OpenBB/blob/develop/examples/financialStatements.ipynb",
40
+ "img": "https://raw.githubusercontent.com/OpenBB-finance/OpenBBTerminal/develop/examples/financialStatements.webp",
41
+ "description":
42
+ "Get started with financial statements in the OpenBB Platform. This notebook compares the data from different providers and demonstrates how to access items within the three main financial statements - balance, cash, and income."
43
+ },
44
+ {
45
+ "title": "Implied Earnings Move",
46
+ "url": "https://github.com/OpenBB-finance/OpenBB/blob/develop/examples/impliedEarningsMove.ipynb",
47
+ "img": "https://raw.githubusercontent.com/OpenBB-finance/OpenBBTerminal/develop/examples/impliedEarningsMove.webp",
48
+ "description":
49
+ "Calculate the implied earnings move using options prices. This notebook demonstrates how to get the data from free sources and apply filters to arrive at the expected move, as a percent, in either direction."
50
+ },
51
+ {
52
+ "title": "Streamlit News Headlines",
53
+ "url": "https://github.com/OpenBB-finance/OpenBB/blob/develop/examples/streamlit/news.py",
54
+ "img": "https://raw.githubusercontent.com/OpenBB-finance/OpenBBTerminal/develop/examples/streamlit_news.webp",
55
+ "description": "An example Streamlit dashboard for news headlines with data from Biztoc, Benzinga, FMP, Intrinio, and Tiingo."
56
+ }
57
+ ]
examples/copperToGoldRatio.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
examples/currencyExchangeRateForecasting.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
examples/financialStatements.ipynb ADDED
@@ -0,0 +1,1244 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# Financial Statements in the OpenBB Platform\n",
8
+ "\n",
9
+ "OpenBB Platform data extensions provide access to financial statements as quarterly or annual. There are also endpoints for ratios and other common non-GAAP metrics. Most data providers require a subscription to access all data. Refer to the website of a specific provider for details on entitlements and coverage.\n",
10
+ "\n",
11
+ "Financial statement functions are grouped under the `obb.equity.fundamental` module.\n",
12
+ "\n",
13
+ "## Endpoints\n",
14
+ "\n",
15
+ "The typical financial statements consist of three endpoints:\n",
16
+ "\n",
17
+ "- Balance Sheet: `obb.equity.fundamental.balance()`\n",
18
+ "- Income Statement: `obb.equity.fundamental.income()`\n",
19
+ "- Cash Flow Statement: `obb.equity.fundamental.cash()`\n",
20
+ "\n",
21
+ "The main parameters are:\n",
22
+ "\n",
23
+ "- `symbol`: The company's symbol.\n",
24
+ "- `period`: 'annual' or 'quarter'. Default is 'annual'.\n",
25
+ "- `limit`: Limit the number of results returned, from the latest. Default is 5. For perspective, 150 will go back to 1985. The amount of historical records varies by provider.\n",
26
+ "\n",
27
+ "### Field Names\n",
28
+ "\n",
29
+ "Some considerations to keep in mind when working with financial statements data are:\n",
30
+ "\n",
31
+ "- Every data provider has their own way of parsing and organizing the three financial statements.\n",
32
+ "- Items within each statement will vary by source and by the type of company reporting.\n",
33
+ "- Names of line items will vary by source.\n",
34
+ "- \"Date\" values may differ because they are from the period starting/ending or date of reporting.\n",
35
+ "\n",
36
+ "This example highlights how different providers will have different labels for compnay facts.\n",
37
+ "\n",
38
+ "\n",
39
+ "**Note**: API Keys are required for FMP, Intrinio, and Polygon."
40
+ ]
41
+ },
42
+ {
43
+ "cell_type": "code",
44
+ "execution_count": 48,
45
+ "metadata": {},
46
+ "outputs": [],
47
+ "source": [
48
+ "import pandas as pd\n",
49
+ "from openbb import obb"
50
+ ]
51
+ },
52
+ {
53
+ "cell_type": "code",
54
+ "execution_count": 49,
55
+ "metadata": {},
56
+ "outputs": [
57
+ {
58
+ "data": {
59
+ "text/html": [
60
+ "<div>\n",
61
+ "<style scoped>\n",
62
+ " .dataframe tbody tr th:only-of-type {\n",
63
+ " vertical-align: middle;\n",
64
+ " }\n",
65
+ "\n",
66
+ " .dataframe tbody tr th {\n",
67
+ " vertical-align: top;\n",
68
+ " }\n",
69
+ "\n",
70
+ " .dataframe thead th {\n",
71
+ " text-align: right;\n",
72
+ " }\n",
73
+ "</style>\n",
74
+ "<table border=\"1\" class=\"dataframe\">\n",
75
+ " <thead>\n",
76
+ " <tr style=\"text-align: right;\">\n",
77
+ " <th></th>\n",
78
+ " <th>yfinance</th>\n",
79
+ " <th>fmp</th>\n",
80
+ " <th>intrinio</th>\n",
81
+ " <th>polygon</th>\n",
82
+ " </tr>\n",
83
+ " </thead>\n",
84
+ " <tbody>\n",
85
+ " <tr>\n",
86
+ " <th>0</th>\n",
87
+ " <td>5.535600e+10</td>\n",
88
+ " <td>5.535600e+10</td>\n",
89
+ " <td>5.535600e+10</td>\n",
90
+ " <td>5.535600e+10</td>\n",
91
+ " </tr>\n",
92
+ " <tr>\n",
93
+ " <th>1</th>\n",
94
+ " <td>5.333500e+10</td>\n",
95
+ " <td>5.333500e+10</td>\n",
96
+ " <td>5.333500e+10</td>\n",
97
+ " <td>5.333500e+10</td>\n",
98
+ " </tr>\n",
99
+ " <tr>\n",
100
+ " <th>2</th>\n",
101
+ " <td>5.381100e+10</td>\n",
102
+ " <td>5.381100e+10</td>\n",
103
+ " <td>5.381100e+10</td>\n",
104
+ " <td>5.381100e+10</td>\n",
105
+ " </tr>\n",
106
+ " </tbody>\n",
107
+ "</table>\n",
108
+ "</div>"
109
+ ],
110
+ "text/plain": [
111
+ " yfinance fmp intrinio polygon\n",
112
+ "0 5.535600e+10 5.535600e+10 5.535600e+10 5.535600e+10\n",
113
+ "1 5.333500e+10 5.333500e+10 5.333500e+10 5.333500e+10\n",
114
+ "2 5.381100e+10 5.381100e+10 5.381100e+10 5.381100e+10"
115
+ ]
116
+ },
117
+ "execution_count": 49,
118
+ "metadata": {},
119
+ "output_type": "execute_result"
120
+ }
121
+ ],
122
+ "source": [
123
+ "df = pd.DataFrame()\n",
124
+ "\n",
125
+ "df[\"yfinance\"] = (\n",
126
+ " obb.equity.fundamental.balance(\n",
127
+ " \"TGT\", provider=\"yfinance\"\n",
128
+ " ) # There is no limit for yFinance, historical data is limited.\n",
129
+ " .to_df()\n",
130
+ " .get(\"total_assets\")\n",
131
+ " .head(3)\n",
132
+ ")\n",
133
+ "\n",
134
+ "df[\"fmp\"] = (\n",
135
+ " obb.equity.fundamental.balance(\"TGT\", provider=\"fmp\", limit=3)\n",
136
+ " .to_df()\n",
137
+ " .get(\"total_assets\")\n",
138
+ ")\n",
139
+ "\n",
140
+ "df[\"intrinio\"] = (\n",
141
+ " obb.equity.fundamental.balance(\"TGT\", provider=\"intrinio\", limit=3)\n",
142
+ " .to_df()\n",
143
+ " .get(\"total_assets\")\n",
144
+ ")\n",
145
+ "\n",
146
+ "df[\"polygon\"] = (\n",
147
+ " obb.equity.fundamental.balance(\"TGT\", provider=\"polygon\", limit=3)\n",
148
+ " .to_df()\n",
149
+ " .get(\"total_assets\")\n",
150
+ ")\n",
151
+ "\n",
152
+ "df"
153
+ ]
154
+ },
155
+ {
156
+ "cell_type": "markdown",
157
+ "metadata": {},
158
+ "source": [
159
+ "### Weighted Average Shares Outstanding\n",
160
+ "\n",
161
+ "This key metric will be found under the income statement. It might also be called, 'basic', and the numbers do not include authorized but unissued shares. A declining count over time is a sign that the company is returning capital to shareholders in the form of buy backs. Under ideal circumstances, it is more capital-efficient, for both company and shareholders, because distributions are double-taxed. The company pays income tax on paid dividends, and the beneficiary pays income tax again on receipt.\n",
162
+ "\n",
163
+ "A company will disclose how many shares are outstanding at the end of the period as a weighted average over the reporting period - three months.\n",
164
+ "\n",
165
+ "Let's take a look at Target. To make the numbers easier to read, we'll divide the entire column by one million."
166
+ ]
167
+ },
168
+ {
169
+ "cell_type": "code",
170
+ "execution_count": 50,
171
+ "metadata": {},
172
+ "outputs": [
173
+ {
174
+ "data": {
175
+ "text/plain": [
176
+ "0 462.5\n",
177
+ "Name: weighted_average_basic_shares_outstanding, dtype: float64"
178
+ ]
179
+ },
180
+ "metadata": {},
181
+ "output_type": "display_data"
182
+ },
183
+ {
184
+ "data": {
185
+ "text/plain": [
186
+ "149 1169.248\n",
187
+ "Name: weighted_average_basic_shares_outstanding, dtype: float64"
188
+ ]
189
+ },
190
+ "metadata": {},
191
+ "output_type": "display_data"
192
+ }
193
+ ],
194
+ "source": [
195
+ "data = obb.equity.fundamental.income(\n",
196
+ " \"TGT\", provider=\"fmp\", limit=150, period=\"quarter\"\n",
197
+ ").to_df()\n",
198
+ "\n",
199
+ "shares = data[\"weighted_average_basic_shares_outstanding\"] / 1000000\n",
200
+ "\n",
201
+ "display(shares.head(1))\n",
202
+ "\n",
203
+ "display(shares.tail(1))"
204
+ ]
205
+ },
206
+ {
207
+ "cell_type": "markdown",
208
+ "metadata": {},
209
+ "source": [
210
+ "Thirty-seven years later, the share count is approaching a two-thirds reduction. 12.2% over the past five years. In four reporting periods, 1.3 million shares have been taken out of the float."
211
+ ]
212
+ },
213
+ {
214
+ "cell_type": "code",
215
+ "execution_count": 51,
216
+ "metadata": {},
217
+ "outputs": [
218
+ {
219
+ "data": {
220
+ "text/plain": [
221
+ "0.3362834285714287"
222
+ ]
223
+ },
224
+ "metadata": {},
225
+ "output_type": "display_data"
226
+ },
227
+ {
228
+ "data": {
229
+ "text/plain": [
230
+ "-65.75199999999995"
231
+ ]
232
+ },
233
+ "metadata": {},
234
+ "output_type": "display_data"
235
+ }
236
+ ],
237
+ "source": [
238
+ "display(shares.pct_change(20).iloc[-1])\n",
239
+ "\n",
240
+ "display(shares.iloc[-4] - shares.iloc[-1])"
241
+ ]
242
+ },
243
+ {
244
+ "cell_type": "markdown",
245
+ "metadata": {},
246
+ "source": [
247
+ "With an average closing price of $143.37, that represents approximately $190M in buy backs."
248
+ ]
249
+ },
250
+ {
251
+ "cell_type": "code",
252
+ "execution_count": 52,
253
+ "metadata": {},
254
+ "outputs": [
255
+ {
256
+ "data": {
257
+ "text/plain": [
258
+ "190.75"
259
+ ]
260
+ },
261
+ "execution_count": 52,
262
+ "metadata": {},
263
+ "output_type": "execute_result"
264
+ }
265
+ ],
266
+ "source": [
267
+ "price = obb.equity.price.historical(\n",
268
+ " \"TGT\", start_date=\"2022-10-01\", provider=\"fmp\"\n",
269
+ ").to_df()\n",
270
+ "\n",
271
+ "round((price[\"close\"].mean() * 1300000) / 1000000, 2)"
272
+ ]
273
+ },
274
+ {
275
+ "cell_type": "markdown",
276
+ "metadata": {},
277
+ "source": [
278
+ "### Dividends Paid\n",
279
+ "\n",
280
+ "Dividends paid is in the cash flow statement. We can calculate the amount-per-share with the reported data."
281
+ ]
282
+ },
283
+ {
284
+ "cell_type": "code",
285
+ "execution_count": 54,
286
+ "metadata": {},
287
+ "outputs": [
288
+ {
289
+ "data": {
290
+ "text/plain": [
291
+ "136 0.040339\n",
292
+ "137 0.023793\n",
293
+ "138 0.020690\n",
294
+ "139 0.022969\n",
295
+ "Name: div_per_share, dtype: float64"
296
+ ]
297
+ },
298
+ "execution_count": 54,
299
+ "metadata": {},
300
+ "output_type": "execute_result"
301
+ }
302
+ ],
303
+ "source": [
304
+ "dividends = obb.equity.fundamental.cash(\n",
305
+ " \"TGT\", provider=\"fmp\", limit=150, period=\"quarter\"\n",
306
+ ").to_df()[[\"payment_of_dividends\"]]\n",
307
+ "\n",
308
+ "dividends[\"shares\"] = data[[\"weighted_average_basic_shares_outstanding\"]]\n",
309
+ "dividends[\"div_per_share\"] = abs(\n",
310
+ " dividends[\"payment_of_dividends\"] / dividends[\"shares\"]\n",
311
+ ")\n",
312
+ "\n",
313
+ "dividends[\"div_per_share\"].tail(4)"
314
+ ]
315
+ },
316
+ {
317
+ "cell_type": "markdown",
318
+ "metadata": {},
319
+ "source": [
320
+ "This can be compared against the real amounts paid to common share holders, as announced. Note that the dates above represent the report date, and that dividends paid are attributed to the quarter they were paid in. The value from \"2023-01-28\" equates to the fourth quarter of 2022."
321
+ ]
322
+ },
323
+ {
324
+ "cell_type": "code",
325
+ "execution_count": 55,
326
+ "metadata": {},
327
+ "outputs": [
328
+ {
329
+ "data": {
330
+ "text/html": [
331
+ "<div>\n",
332
+ "<style scoped>\n",
333
+ " .dataframe tbody tr th:only-of-type {\n",
334
+ " vertical-align: middle;\n",
335
+ " }\n",
336
+ "\n",
337
+ " .dataframe tbody tr th {\n",
338
+ " vertical-align: top;\n",
339
+ " }\n",
340
+ "\n",
341
+ " .dataframe thead th {\n",
342
+ " text-align: right;\n",
343
+ " }\n",
344
+ "</style>\n",
345
+ "<table border=\"1\" class=\"dataframe\">\n",
346
+ " <thead>\n",
347
+ " <tr style=\"text-align: right;\">\n",
348
+ " <th></th>\n",
349
+ " <th>amount</th>\n",
350
+ " </tr>\n",
351
+ " <tr>\n",
352
+ " <th>ex_dividend_date</th>\n",
353
+ " <th></th>\n",
354
+ " </tr>\n",
355
+ " </thead>\n",
356
+ " <tbody>\n",
357
+ " <tr>\n",
358
+ " <th>2023-08-15</th>\n",
359
+ " <td>1.10</td>\n",
360
+ " </tr>\n",
361
+ " <tr>\n",
362
+ " <th>2023-05-16</th>\n",
363
+ " <td>1.08</td>\n",
364
+ " </tr>\n",
365
+ " <tr>\n",
366
+ " <th>2023-02-14</th>\n",
367
+ " <td>1.08</td>\n",
368
+ " </tr>\n",
369
+ " <tr>\n",
370
+ " <th>2022-11-15</th>\n",
371
+ " <td>1.08</td>\n",
372
+ " </tr>\n",
373
+ " </tbody>\n",
374
+ "</table>\n",
375
+ "</div>"
376
+ ],
377
+ "text/plain": [
378
+ " amount\n",
379
+ "ex_dividend_date \n",
380
+ "2023-08-15 1.10\n",
381
+ "2023-05-16 1.08\n",
382
+ "2023-02-14 1.08\n",
383
+ "2022-11-15 1.08"
384
+ ]
385
+ },
386
+ "execution_count": 55,
387
+ "metadata": {},
388
+ "output_type": "execute_result"
389
+ }
390
+ ],
391
+ "source": [
392
+ "data = obb.equity.fundamental.dividends(\"TGT\", provider=\"fmp\").to_df()[\n",
393
+ " [\"ex_dividend_date\", \"amount\"]\n",
394
+ "]\n",
395
+ "data.ex_dividend_date = data.ex_dividend_date.astype(str)\n",
396
+ "data.set_index(\"ex_dividend_date\").loc[\"2023-08-15\":\"2022-11-15\"]"
397
+ ]
398
+ },
399
+ {
400
+ "cell_type": "markdown",
401
+ "metadata": {},
402
+ "source": [
403
+ "The numbers check out, and the $2B paid to investors over four quarters is more than ten times the $190M returned through share buy backs.\n",
404
+ "\n",
405
+ "### Financial Attributes\n",
406
+ "\n",
407
+ "The `openbb-intrinio` data extension has an endpoint for extracting a single fact from financial statements. There is a helper function for looking up the correct `tag`.\n",
408
+ "\n",
409
+ "**Note:** Intrinio does not offer a free API level with access to data.\n",
410
+ "\n",
411
+ "#### Search Financial Attributes\n",
412
+ "\n",
413
+ "Search attributes by keyword."
414
+ ]
415
+ },
416
+ {
417
+ "cell_type": "code",
418
+ "execution_count": 10,
419
+ "metadata": {},
420
+ "outputs": [
421
+ {
422
+ "data": {
423
+ "text/html": [
424
+ "<div>\n",
425
+ "<style scoped>\n",
426
+ " .dataframe tbody tr th:only-of-type {\n",
427
+ " vertical-align: middle;\n",
428
+ " }\n",
429
+ "\n",
430
+ " .dataframe tbody tr th {\n",
431
+ " vertical-align: top;\n",
432
+ " }\n",
433
+ "\n",
434
+ " .dataframe thead th {\n",
435
+ " text-align: right;\n",
436
+ " }\n",
437
+ "</style>\n",
438
+ "<table border=\"1\" class=\"dataframe\">\n",
439
+ " <thead>\n",
440
+ " <tr style=\"text-align: right;\">\n",
441
+ " <th></th>\n",
442
+ " <th>id</th>\n",
443
+ " <th>name</th>\n",
444
+ " <th>tag</th>\n",
445
+ " <th>statement_code</th>\n",
446
+ " <th>statement_type</th>\n",
447
+ " <th>type</th>\n",
448
+ " <th>unit</th>\n",
449
+ " <th>parent_name</th>\n",
450
+ " <th>sequence</th>\n",
451
+ " <th>factor</th>\n",
452
+ " <th>transaction</th>\n",
453
+ " </tr>\n",
454
+ " </thead>\n",
455
+ " <tbody>\n",
456
+ " <tr>\n",
457
+ " <th>0</th>\n",
458
+ " <td>tag_BgkbWy</td>\n",
459
+ " <td>Market Capitalization</td>\n",
460
+ " <td>marketcap</td>\n",
461
+ " <td>calculations</td>\n",
462
+ " <td>industrial</td>\n",
463
+ " <td>valuation</td>\n",
464
+ " <td>usd</td>\n",
465
+ " <td>NaN</td>\n",
466
+ " <td>NaN</td>\n",
467
+ " <td>NaN</td>\n",
468
+ " <td>NaN</td>\n",
469
+ " </tr>\n",
470
+ " <tr>\n",
471
+ " <th>1</th>\n",
472
+ " <td>tag_kylOqz</td>\n",
473
+ " <td>Market Capitalization</td>\n",
474
+ " <td>marketcap</td>\n",
475
+ " <td>calculations</td>\n",
476
+ " <td>financial</td>\n",
477
+ " <td>valuation</td>\n",
478
+ " <td>usd</td>\n",
479
+ " <td>NaN</td>\n",
480
+ " <td>NaN</td>\n",
481
+ " <td>NaN</td>\n",
482
+ " <td>NaN</td>\n",
483
+ " </tr>\n",
484
+ " <tr>\n",
485
+ " <th>2</th>\n",
486
+ " <td>tag_XLRlqy</td>\n",
487
+ " <td>Market Sector</td>\n",
488
+ " <td>market_sector</td>\n",
489
+ " <td>current</td>\n",
490
+ " <td>NaN</td>\n",
491
+ " <td>security</td>\n",
492
+ " <td>string</td>\n",
493
+ " <td>NaN</td>\n",
494
+ " <td>NaN</td>\n",
495
+ " <td>NaN</td>\n",
496
+ " <td>NaN</td>\n",
497
+ " </tr>\n",
498
+ " <tr>\n",
499
+ " <th>3</th>\n",
500
+ " <td>tag_2gBA8y</td>\n",
501
+ " <td>Market Category</td>\n",
502
+ " <td>market_category</td>\n",
503
+ " <td>current</td>\n",
504
+ " <td>NaN</td>\n",
505
+ " <td>security</td>\n",
506
+ " <td>string</td>\n",
507
+ " <td>NaN</td>\n",
508
+ " <td>NaN</td>\n",
509
+ " <td>NaN</td>\n",
510
+ " <td>NaN</td>\n",
511
+ " </tr>\n",
512
+ " <tr>\n",
513
+ " <th>4</th>\n",
514
+ " <td>tag_DzonXe</td>\n",
515
+ " <td>Marketing Expense</td>\n",
516
+ " <td>marketingexpense</td>\n",
517
+ " <td>income_statement</td>\n",
518
+ " <td>industrial</td>\n",
519
+ " <td>income_statement_metric</td>\n",
520
+ " <td>usd</td>\n",
521
+ " <td>totaloperatingexpenses</td>\n",
522
+ " <td>9.0</td>\n",
523
+ " <td>+</td>\n",
524
+ " <td>debit</td>\n",
525
+ " </tr>\n",
526
+ " <tr>\n",
527
+ " <th>...</th>\n",
528
+ " <td>...</td>\n",
529
+ " <td>...</td>\n",
530
+ " <td>...</td>\n",
531
+ " <td>...</td>\n",
532
+ " <td>...</td>\n",
533
+ " <td>...</td>\n",
534
+ " <td>...</td>\n",
535
+ " <td>...</td>\n",
536
+ " <td>...</td>\n",
537
+ " <td>...</td>\n",
538
+ " <td>...</td>\n",
539
+ " </tr>\n",
540
+ " <tr>\n",
541
+ " <th>95</th>\n",
542
+ " <td>tag_nzJAmX</td>\n",
543
+ " <td>Total Long-Term Debt</td>\n",
544
+ " <td>ltdebtandcapleases</td>\n",
545
+ " <td>calculations</td>\n",
546
+ " <td>financial</td>\n",
547
+ " <td>metric</td>\n",
548
+ " <td>usd</td>\n",
549
+ " <td>NaN</td>\n",
550
+ " <td>NaN</td>\n",
551
+ " <td>NaN</td>\n",
552
+ " <td>NaN</td>\n",
553
+ " </tr>\n",
554
+ " <tr>\n",
555
+ " <th>96</th>\n",
556
+ " <td>tag_9XaL5g</td>\n",
557
+ " <td>Other Net Changes in Cash</td>\n",
558
+ " <td>othernetchangesincash</td>\n",
559
+ " <td>cash_flow_statement</td>\n",
560
+ " <td>industrial</td>\n",
561
+ " <td>cash_flow_statement_metric</td>\n",
562
+ " <td>usd</td>\n",
563
+ " <td>netchangeincash</td>\n",
564
+ " <td>33.0</td>\n",
565
+ " <td>+</td>\n",
566
+ " <td>debit</td>\n",
567
+ " </tr>\n",
568
+ " <tr>\n",
569
+ " <th>97</th>\n",
570
+ " <td>tag_5X7p6z</td>\n",
571
+ " <td>Other Net Changes in Cash</td>\n",
572
+ " <td>othernetchangesincash</td>\n",
573
+ " <td>cash_flow_statement</td>\n",
574
+ " <td>financial</td>\n",
575
+ " <td>cash_flow_statement_metric</td>\n",
576
+ " <td>usd</td>\n",
577
+ " <td>netchangeincash</td>\n",
578
+ " <td>37.0</td>\n",
579
+ " <td>+</td>\n",
580
+ " <td>debit</td>\n",
581
+ " </tr>\n",
582
+ " <tr>\n",
583
+ " <th>98</th>\n",
584
+ " <td>tag_qzEwng</td>\n",
585
+ " <td>Changes in Operating Assets and Liabilities, net</td>\n",
586
+ " <td>increasedecreaseinoperatingcapital</td>\n",
587
+ " <td>cash_flow_statement</td>\n",
588
+ " <td>financial</td>\n",
589
+ " <td>cash_flow_statement_metric</td>\n",
590
+ " <td>usd</td>\n",
591
+ " <td>netcashfromcontinuingoperatingactivities</td>\n",
592
+ " <td>8.0</td>\n",
593
+ " <td>+</td>\n",
594
+ " <td>debit</td>\n",
595
+ " </tr>\n",
596
+ " <tr>\n",
597
+ " <th>99</th>\n",
598
+ " <td>tag_pgVB2g</td>\n",
599
+ " <td>Changes in Operating Assets and Liabilities, net</td>\n",
600
+ " <td>increasedecreaseinoperatingcapital</td>\n",
601
+ " <td>cash_flow_statement</td>\n",
602
+ " <td>industrial</td>\n",
603
+ " <td>cash_flow_statement_metric</td>\n",
604
+ " <td>usd</td>\n",
605
+ " <td>netcashfromcontinuingoperatingactivities</td>\n",
606
+ " <td>7.0</td>\n",
607
+ " <td>+</td>\n",
608
+ " <td>debit</td>\n",
609
+ " </tr>\n",
610
+ " </tbody>\n",
611
+ "</table>\n",
612
+ "<p>100 rows × 11 columns</p>\n",
613
+ "</div>"
614
+ ],
615
+ "text/plain": [
616
+ " id name \\\n",
617
+ "0 tag_BgkbWy Market Capitalization \n",
618
+ "1 tag_kylOqz Market Capitalization \n",
619
+ "2 tag_XLRlqy Market Sector \n",
620
+ "3 tag_2gBA8y Market Category \n",
621
+ "4 tag_DzonXe Marketing Expense \n",
622
+ ".. ... ... \n",
623
+ "95 tag_nzJAmX Total Long-Term Debt \n",
624
+ "96 tag_9XaL5g Other Net Changes in Cash \n",
625
+ "97 tag_5X7p6z Other Net Changes in Cash \n",
626
+ "98 tag_qzEwng Changes in Operating Assets and Liabilities, net \n",
627
+ "99 tag_pgVB2g Changes in Operating Assets and Liabilities, net \n",
628
+ "\n",
629
+ " tag statement_code statement_type \\\n",
630
+ "0 marketcap calculations industrial \n",
631
+ "1 marketcap calculations financial \n",
632
+ "2 market_sector current NaN \n",
633
+ "3 market_category current NaN \n",
634
+ "4 marketingexpense income_statement industrial \n",
635
+ ".. ... ... ... \n",
636
+ "95 ltdebtandcapleases calculations financial \n",
637
+ "96 othernetchangesincash cash_flow_statement industrial \n",
638
+ "97 othernetchangesincash cash_flow_statement financial \n",
639
+ "98 increasedecreaseinoperatingcapital cash_flow_statement financial \n",
640
+ "99 increasedecreaseinoperatingcapital cash_flow_statement industrial \n",
641
+ "\n",
642
+ " type unit \\\n",
643
+ "0 valuation usd \n",
644
+ "1 valuation usd \n",
645
+ "2 security string \n",
646
+ "3 security string \n",
647
+ "4 income_statement_metric usd \n",
648
+ ".. ... ... \n",
649
+ "95 metric usd \n",
650
+ "96 cash_flow_statement_metric usd \n",
651
+ "97 cash_flow_statement_metric usd \n",
652
+ "98 cash_flow_statement_metric usd \n",
653
+ "99 cash_flow_statement_metric usd \n",
654
+ "\n",
655
+ " parent_name sequence factor transaction \n",
656
+ "0 NaN NaN NaN NaN \n",
657
+ "1 NaN NaN NaN NaN \n",
658
+ "2 NaN NaN NaN NaN \n",
659
+ "3 NaN NaN NaN NaN \n",
660
+ "4 totaloperatingexpenses 9.0 + debit \n",
661
+ ".. ... ... ... ... \n",
662
+ "95 NaN NaN NaN NaN \n",
663
+ "96 netchangeincash 33.0 + debit \n",
664
+ "97 netchangeincash 37.0 + debit \n",
665
+ "98 netcashfromcontinuingoperatingactivities 8.0 + debit \n",
666
+ "99 netcashfromcontinuingoperatingactivities 7.0 + debit \n",
667
+ "\n",
668
+ "[100 rows x 11 columns]"
669
+ ]
670
+ },
671
+ "execution_count": 10,
672
+ "metadata": {},
673
+ "output_type": "execute_result"
674
+ }
675
+ ],
676
+ "source": [
677
+ "(obb.equity.fundamental.search_attributes(\"marketcap\", provider=\"intrinio\").to_df())"
678
+ ]
679
+ },
680
+ {
681
+ "cell_type": "markdown",
682
+ "metadata": {},
683
+ "source": [
684
+ "The `tag` is what we need, in this case it is what we searched for."
685
+ ]
686
+ },
687
+ {
688
+ "cell_type": "code",
689
+ "execution_count": 20,
690
+ "metadata": {},
691
+ "outputs": [
692
+ {
693
+ "data": {
694
+ "text/html": [
695
+ "<div>\n",
696
+ "<style scoped>\n",
697
+ " .dataframe tbody tr th:only-of-type {\n",
698
+ " vertical-align: middle;\n",
699
+ " }\n",
700
+ "\n",
701
+ " .dataframe tbody tr th {\n",
702
+ " vertical-align: top;\n",
703
+ " }\n",
704
+ "\n",
705
+ " .dataframe thead th {\n",
706
+ " text-align: right;\n",
707
+ " }\n",
708
+ "</style>\n",
709
+ "<table border=\"1\" class=\"dataframe\">\n",
710
+ " <thead>\n",
711
+ " <tr style=\"text-align: right;\">\n",
712
+ " <th></th>\n",
713
+ " <th>symbol</th>\n",
714
+ " <th>tag</th>\n",
715
+ " <th>value</th>\n",
716
+ " </tr>\n",
717
+ " <tr>\n",
718
+ " <th>date</th>\n",
719
+ " <th></th>\n",
720
+ " <th></th>\n",
721
+ " <th></th>\n",
722
+ " </tr>\n",
723
+ " </thead>\n",
724
+ " <tbody>\n",
725
+ " <tr>\n",
726
+ " <th>2023-09-30</th>\n",
727
+ " <td>TGT</td>\n",
728
+ " <td>marketcap</td>\n",
729
+ " <td>4.951153e+10</td>\n",
730
+ " </tr>\n",
731
+ " <tr>\n",
732
+ " <th>2023-12-31</th>\n",
733
+ " <td>TGT</td>\n",
734
+ " <td>marketcap</td>\n",
735
+ " <td>6.443403e+10</td>\n",
736
+ " </tr>\n",
737
+ " <tr>\n",
738
+ " <th>2024-03-31</th>\n",
739
+ " <td>TGT</td>\n",
740
+ " <td>marketcap</td>\n",
741
+ " <td>8.082004e+10</td>\n",
742
+ " </tr>\n",
743
+ " <tr>\n",
744
+ " <th>2024-06-30</th>\n",
745
+ " <td>TGT</td>\n",
746
+ " <td>marketcap</td>\n",
747
+ " <td>6.814283e+10</td>\n",
748
+ " </tr>\n",
749
+ " <tr>\n",
750
+ " <th>2024-08-22</th>\n",
751
+ " <td>TGT</td>\n",
752
+ " <td>marketcap</td>\n",
753
+ " <td>7.387608e+10</td>\n",
754
+ " </tr>\n",
755
+ " </tbody>\n",
756
+ "</table>\n",
757
+ "</div>"
758
+ ],
759
+ "text/plain": [
760
+ " symbol tag value\n",
761
+ "date \n",
762
+ "2023-09-30 TGT marketcap 4.951153e+10\n",
763
+ "2023-12-31 TGT marketcap 6.443403e+10\n",
764
+ "2024-03-31 TGT marketcap 8.082004e+10\n",
765
+ "2024-06-30 TGT marketcap 6.814283e+10\n",
766
+ "2024-08-22 TGT marketcap 7.387608e+10"
767
+ ]
768
+ },
769
+ "execution_count": 20,
770
+ "metadata": {},
771
+ "output_type": "execute_result"
772
+ }
773
+ ],
774
+ "source": [
775
+ "marketcap = obb.equity.fundamental.historical_attributes(\n",
776
+ " symbol=\"TGT\", tag=\"marketcap\", frequency=\"quarterly\", provider=\"intrinio\"\n",
777
+ ").to_df()\n",
778
+ "\n",
779
+ "marketcap.tail(5)"
780
+ ]
781
+ },
782
+ {
783
+ "cell_type": "markdown",
784
+ "metadata": {},
785
+ "source": [
786
+ "Doing some quick math, and ignoring the most recent value, we can see that the market cap of Target was down nearly a quarter over the last four reporting periods."
787
+ ]
788
+ },
789
+ {
790
+ "cell_type": "code",
791
+ "execution_count": 40,
792
+ "metadata": {},
793
+ "outputs": [
794
+ {
795
+ "data": {
796
+ "text/plain": [
797
+ "-0.243767327909974"
798
+ ]
799
+ },
800
+ "execution_count": 40,
801
+ "metadata": {},
802
+ "output_type": "execute_result"
803
+ }
804
+ ],
805
+ "source": [
806
+ "marketcap.index = marketcap.index.astype(str)\n",
807
+ "(\n",
808
+ " (marketcap.loc[\"2023-09-30\"].value - marketcap.loc[\"2022-12-31\"].value)\n",
809
+ " / marketcap.loc[\"2022-12-31\"].value\n",
810
+ ")"
811
+ ]
812
+ },
813
+ {
814
+ "cell_type": "markdown",
815
+ "metadata": {},
816
+ "source": [
817
+ "Historial market cap is also available as a daily metric from FMP. We can resample it as quarterly to approximate the same results."
818
+ ]
819
+ },
820
+ {
821
+ "cell_type": "code",
822
+ "execution_count": 43,
823
+ "metadata": {},
824
+ "outputs": [
825
+ {
826
+ "data": {
827
+ "text/html": [
828
+ "<div>\n",
829
+ "<style scoped>\n",
830
+ " .dataframe tbody tr th:only-of-type {\n",
831
+ " vertical-align: middle;\n",
832
+ " }\n",
833
+ "\n",
834
+ " .dataframe tbody tr th {\n",
835
+ " vertical-align: top;\n",
836
+ " }\n",
837
+ "\n",
838
+ " .dataframe thead th {\n",
839
+ " text-align: right;\n",
840
+ " }\n",
841
+ "</style>\n",
842
+ "<table border=\"1\" class=\"dataframe\">\n",
843
+ " <thead>\n",
844
+ " <tr style=\"text-align: right;\">\n",
845
+ " <th></th>\n",
846
+ " <th>market_cap</th>\n",
847
+ " </tr>\n",
848
+ " <tr>\n",
849
+ " <th>date</th>\n",
850
+ " <th></th>\n",
851
+ " </tr>\n",
852
+ " </thead>\n",
853
+ " <tbody>\n",
854
+ " <tr>\n",
855
+ " <th>2022-03-31</th>\n",
856
+ " <td>98470080000</td>\n",
857
+ " </tr>\n",
858
+ " <tr>\n",
859
+ " <th>2022-06-30</th>\n",
860
+ " <td>65177644999</td>\n",
861
+ " </tr>\n",
862
+ " <tr>\n",
863
+ " <th>2022-09-30</th>\n",
864
+ " <td>68303916999</td>\n",
865
+ " </tr>\n",
866
+ " <tr>\n",
867
+ " <th>2022-12-31</th>\n",
868
+ " <td>68603112000</td>\n",
869
+ " </tr>\n",
870
+ " <tr>\n",
871
+ " <th>2023-03-31</th>\n",
872
+ " <td>76338867000</td>\n",
873
+ " </tr>\n",
874
+ " <tr>\n",
875
+ " <th>2023-06-30</th>\n",
876
+ " <td>60885040000</td>\n",
877
+ " </tr>\n",
878
+ " <tr>\n",
879
+ " <th>2023-09-30</th>\n",
880
+ " <td>51039112000</td>\n",
881
+ " </tr>\n",
882
+ " <tr>\n",
883
+ " <th>2023-12-31</th>\n",
884
+ " <td>65755313999</td>\n",
885
+ " </tr>\n",
886
+ " <tr>\n",
887
+ " <th>2024-03-31</th>\n",
888
+ " <td>81906462000</td>\n",
889
+ " </tr>\n",
890
+ " <tr>\n",
891
+ " <th>2024-06-30</th>\n",
892
+ " <td>68424088000</td>\n",
893
+ " </tr>\n",
894
+ " <tr>\n",
895
+ " <th>2024-09-30</th>\n",
896
+ " <td>73653125000</td>\n",
897
+ " </tr>\n",
898
+ " </tbody>\n",
899
+ "</table>\n",
900
+ "</div>"
901
+ ],
902
+ "text/plain": [
903
+ " market_cap\n",
904
+ "date \n",
905
+ "2022-03-31 98470080000\n",
906
+ "2022-06-30 65177644999\n",
907
+ "2022-09-30 68303916999\n",
908
+ "2022-12-31 68603112000\n",
909
+ "2023-03-31 76338867000\n",
910
+ "2023-06-30 60885040000\n",
911
+ "2023-09-30 51039112000\n",
912
+ "2023-12-31 65755313999\n",
913
+ "2024-03-31 81906462000\n",
914
+ "2024-06-30 68424088000\n",
915
+ "2024-09-30 73653125000"
916
+ ]
917
+ },
918
+ "metadata": {},
919
+ "output_type": "display_data"
920
+ },
921
+ {
922
+ "data": {
923
+ "text/plain": [
924
+ "market_cap -0.256023\n",
925
+ "dtype: float64"
926
+ ]
927
+ },
928
+ "execution_count": 43,
929
+ "metadata": {},
930
+ "output_type": "execute_result"
931
+ }
932
+ ],
933
+ "source": [
934
+ "df = obb.equity.historical_market_cap(\n",
935
+ " \"TGT\", start_date=\"2022-01-01\", provider=\"fmp\"\n",
936
+ ").to_df()\n",
937
+ "\n",
938
+ "resampled = df.copy()\n",
939
+ "resampled.index = pd.to_datetime(resampled.index)\n",
940
+ "resampled = resampled[[\"market_cap\"]]\n",
941
+ "resampled = resampled.resample(\"QE\").last()\n",
942
+ "resampled.index = resampled.index.astype(str)\n",
943
+ "display(resampled)\n",
944
+ "(\n",
945
+ " (resampled.loc[\"2023-09-30\"] - resampled.loc[\"2022-12-31\"])\n",
946
+ " / resampled.loc[\"2022-12-31\"]\n",
947
+ ")"
948
+ ]
949
+ },
950
+ {
951
+ "cell_type": "markdown",
952
+ "metadata": {},
953
+ "source": [
954
+ "## Ratios and Other Metrics\n",
955
+ "\n",
956
+ "Other valuation functions are derivatives of the financial statements, but the data provider does the math. Values are typically ratios between line items, on a per-share basis, or as a percent growth.\n",
957
+ "\n",
958
+ "This data set is where you can find EPS, FCF, P/B, EBIT, quick ratio, etc.\n",
959
+ "\n",
960
+ "### Quick Ratio\n",
961
+ "\n",
962
+ "Target's quick ratio could be one reason why its share price is losing traction against the market. Its ability to pay current obligations is not optimistically reflected in a 0.27 score, approximately 50% below the historical median."
963
+ ]
964
+ },
965
+ {
966
+ "cell_type": "code",
967
+ "execution_count": 56,
968
+ "metadata": {},
969
+ "outputs": [
970
+ {
971
+ "data": {
972
+ "text/plain": [
973
+ "'Current Quick Ratio: 0.8998'"
974
+ ]
975
+ },
976
+ "metadata": {},
977
+ "output_type": "display_data"
978
+ },
979
+ {
980
+ "data": {
981
+ "text/plain": [
982
+ "'Median Quick Ratio: 0.6047'"
983
+ ]
984
+ },
985
+ "metadata": {},
986
+ "output_type": "display_data"
987
+ }
988
+ ],
989
+ "source": [
990
+ "ratios = obb.equity.fundamental.ratios(\"TGT\", limit=50, provider=\"fmp\").to_df()\n",
991
+ "\n",
992
+ "display(f\"Current Quick Ratio: {round(ratios['quick_ratio'].iloc[-1], 4)}\")\n",
993
+ "display(f\"Median Quick Ratio: {round(ratios['quick_ratio'].median(), 4)}\")"
994
+ ]
995
+ },
996
+ {
997
+ "cell_type": "markdown",
998
+ "metadata": {},
999
+ "source": [
1000
+ "### Free Cash Flow Yield\n",
1001
+ "\n",
1002
+ "The `metrics` endpoint, with the `openbb-fmp` data extension, has a field for free cash flow yield. It is calculated by taking the free cash flow per share divided by the current share price. We could arrive at this answer by writing some code, but these types of endpoints do the work so we don't have to. This is part of the value-add that API data distributors provide, they allow you to get straight to work with data.\n",
1003
+ "\n",
1004
+ "We'll use this endpoint to extract the data, and compare with some of Target's competition over the last ten years."
1005
+ ]
1006
+ },
1007
+ {
1008
+ "cell_type": "code",
1009
+ "execution_count": 57,
1010
+ "metadata": {},
1011
+ "outputs": [
1012
+ {
1013
+ "data": {
1014
+ "text/html": [
1015
+ "<div>\n",
1016
+ "<style scoped>\n",
1017
+ " .dataframe tbody tr th:only-of-type {\n",
1018
+ " vertical-align: middle;\n",
1019
+ " }\n",
1020
+ "\n",
1021
+ " .dataframe tbody tr th {\n",
1022
+ " vertical-align: top;\n",
1023
+ " }\n",
1024
+ "\n",
1025
+ " .dataframe thead th {\n",
1026
+ " text-align: right;\n",
1027
+ " }\n",
1028
+ "</style>\n",
1029
+ "<table border=\"1\" class=\"dataframe\">\n",
1030
+ " <thead>\n",
1031
+ " <tr style=\"text-align: right;\">\n",
1032
+ " <th>calendar_year</th>\n",
1033
+ " <th>2023</th>\n",
1034
+ " <th>2022</th>\n",
1035
+ " <th>2021</th>\n",
1036
+ " <th>2020</th>\n",
1037
+ " <th>2019</th>\n",
1038
+ " <th>2018</th>\n",
1039
+ " <th>2017</th>\n",
1040
+ " <th>2016</th>\n",
1041
+ " <th>2015</th>\n",
1042
+ " <th>2014</th>\n",
1043
+ " </tr>\n",
1044
+ " </thead>\n",
1045
+ " <tbody>\n",
1046
+ " <tr>\n",
1047
+ " <th>COST</th>\n",
1048
+ " <td>0.027922</td>\n",
1049
+ " <td>0.014860</td>\n",
1050
+ " <td>0.026582</td>\n",
1051
+ " <td>0.039351</td>\n",
1052
+ " <td>0.025906</td>\n",
1053
+ " <td>0.027438</td>\n",
1054
+ " <td>0.060884</td>\n",
1055
+ " <td>0.008941</td>\n",
1056
+ " <td>0.030741</td>\n",
1057
+ " <td>0.037483</td>\n",
1058
+ " </tr>\n",
1059
+ " <tr>\n",
1060
+ " <th>BJ</th>\n",
1061
+ " <td>0.029338</td>\n",
1062
+ " <td>0.044709</td>\n",
1063
+ " <td>0.067213</td>\n",
1064
+ " <td>0.113551</td>\n",
1065
+ " <td>0.056631</td>\n",
1066
+ " <td>0.091107</td>\n",
1067
+ " <td>0.026186</td>\n",
1068
+ " <td>0.065871</td>\n",
1069
+ " <td>0.016947</td>\n",
1070
+ " <td>NaN</td>\n",
1071
+ " </tr>\n",
1072
+ " <tr>\n",
1073
+ " <th>DLTR</th>\n",
1074
+ " <td>0.018948</td>\n",
1075
+ " <td>0.010756</td>\n",
1076
+ " <td>0.013957</td>\n",
1077
+ " <td>0.075627</td>\n",
1078
+ " <td>0.040338</td>\n",
1079
+ " <td>0.041252</td>\n",
1080
+ " <td>0.034069</td>\n",
1081
+ " <td>0.063465</td>\n",
1082
+ " <td>0.016602</td>\n",
1083
+ " <td>0.041047</td>\n",
1084
+ " </tr>\n",
1085
+ " <tr>\n",
1086
+ " <th>DG</th>\n",
1087
+ " <td>0.023149</td>\n",
1088
+ " <td>0.008256</td>\n",
1089
+ " <td>0.037507</td>\n",
1090
+ " <td>0.058973</td>\n",
1091
+ " <td>0.036922</td>\n",
1092
+ " <td>0.046197</td>\n",
1093
+ " <td>0.042609</td>\n",
1094
+ " <td>0.050776</td>\n",
1095
+ " <td>0.039524</td>\n",
1096
+ " <td>0.046052</td>\n",
1097
+ " </tr>\n",
1098
+ " <tr>\n",
1099
+ " <th>WMT</th>\n",
1100
+ " <td>0.030577</td>\n",
1101
+ " <td>0.028374</td>\n",
1102
+ " <td>0.065467</td>\n",
1103
+ " <td>0.044595</td>\n",
1104
+ " <td>0.062030</td>\n",
1105
+ " <td>0.057280</td>\n",
1106
+ " <td>0.101023</td>\n",
1107
+ " <td>0.073506</td>\n",
1108
+ " <td>0.059705</td>\n",
1109
+ " <td>NaN</td>\n",
1110
+ " </tr>\n",
1111
+ " <tr>\n",
1112
+ " <th>BIG</th>\n",
1113
+ " <td>-1.856996</td>\n",
1114
+ " <td>-0.624151</td>\n",
1115
+ " <td>0.025262</td>\n",
1116
+ " <td>0.115757</td>\n",
1117
+ " <td>0.069464</td>\n",
1118
+ " <td>-0.111853</td>\n",
1119
+ " <td>0.037219</td>\n",
1120
+ " <td>0.100721</td>\n",
1121
+ " <td>0.110443</td>\n",
1122
+ " <td>0.089253</td>\n",
1123
+ " </tr>\n",
1124
+ " <tr>\n",
1125
+ " <th>M</th>\n",
1126
+ " <td>0.061077</td>\n",
1127
+ " <td>0.050473</td>\n",
1128
+ " <td>0.270980</td>\n",
1129
+ " <td>0.039111</td>\n",
1130
+ " <td>0.091301</td>\n",
1131
+ " <td>0.101426</td>\n",
1132
+ " <td>0.155761</td>\n",
1133
+ " <td>0.098993</td>\n",
1134
+ " <td>0.065634</td>\n",
1135
+ " <td>0.072322</td>\n",
1136
+ " </tr>\n",
1137
+ " <tr>\n",
1138
+ " <th>KSS</th>\n",
1139
+ " <td>0.203512</td>\n",
1140
+ " <td>-0.143961</td>\n",
1141
+ " <td>0.189677</td>\n",
1142
+ " <td>0.147968</td>\n",
1143
+ " <td>0.119492</td>\n",
1144
+ " <td>0.139799</td>\n",
1145
+ " <td>0.096137</td>\n",
1146
+ " <td>0.198790</td>\n",
1147
+ " <td>0.081652</td>\n",
1148
+ " <td>0.110697</td>\n",
1149
+ " </tr>\n",
1150
+ " <tr>\n",
1151
+ " <th>TJX</th>\n",
1152
+ " <td>0.027513</td>\n",
1153
+ " <td>0.023498</td>\n",
1154
+ " <td>0.051975</td>\n",
1155
+ " <td>0.039865</td>\n",
1156
+ " <td>0.049788</td>\n",
1157
+ " <td>0.039930</td>\n",
1158
+ " <td>0.053697</td>\n",
1159
+ " <td>0.043328</td>\n",
1160
+ " <td>0.046442</td>\n",
1161
+ " <td>NaN</td>\n",
1162
+ " </tr>\n",
1163
+ " </tbody>\n",
1164
+ "</table>\n",
1165
+ "</div>"
1166
+ ],
1167
+ "text/plain": [
1168
+ "calendar_year 2023 2022 2021 2020 2019 2018 \\\n",
1169
+ "COST 0.027922 0.014860 0.026582 0.039351 0.025906 0.027438 \n",
1170
+ "BJ 0.029338 0.044709 0.067213 0.113551 0.056631 0.091107 \n",
1171
+ "DLTR 0.018948 0.010756 0.013957 0.075627 0.040338 0.041252 \n",
1172
+ "DG 0.023149 0.008256 0.037507 0.058973 0.036922 0.046197 \n",
1173
+ "WMT 0.030577 0.028374 0.065467 0.044595 0.062030 0.057280 \n",
1174
+ "BIG -1.856996 -0.624151 0.025262 0.115757 0.069464 -0.111853 \n",
1175
+ "M 0.061077 0.050473 0.270980 0.039111 0.091301 0.101426 \n",
1176
+ "KSS 0.203512 -0.143961 0.189677 0.147968 0.119492 0.139799 \n",
1177
+ "TJX 0.027513 0.023498 0.051975 0.039865 0.049788 0.039930 \n",
1178
+ "\n",
1179
+ "calendar_year 2017 2016 2015 2014 \n",
1180
+ "COST 0.060884 0.008941 0.030741 0.037483 \n",
1181
+ "BJ 0.026186 0.065871 0.016947 NaN \n",
1182
+ "DLTR 0.034069 0.063465 0.016602 0.041047 \n",
1183
+ "DG 0.042609 0.050776 0.039524 0.046052 \n",
1184
+ "WMT 0.101023 0.073506 0.059705 NaN \n",
1185
+ "BIG 0.037219 0.100721 0.110443 0.089253 \n",
1186
+ "M 0.155761 0.098993 0.065634 0.072322 \n",
1187
+ "KSS 0.096137 0.198790 0.081652 0.110697 \n",
1188
+ "TJX 0.053697 0.043328 0.046442 NaN "
1189
+ ]
1190
+ },
1191
+ "execution_count": 57,
1192
+ "metadata": {},
1193
+ "output_type": "execute_result"
1194
+ }
1195
+ ],
1196
+ "source": [
1197
+ "# List of other retail chains\n",
1198
+ "tickers = [\"COST\", \"BJ\", \"DLTR\", \"DG\", \"WMT\", \"BIG\", \"M\", \"KSS\", \"TJX\"]\n",
1199
+ "\n",
1200
+ "# Create a column for each.\n",
1201
+ "fcf_yield = pd.DataFrame()\n",
1202
+ "for ticker in tickers:\n",
1203
+ " fcf_yield[ticker] = (\n",
1204
+ " obb.equity.fundamental.metrics(\n",
1205
+ " ticker, provider=\"fmp\", period=\"annual\", limit=10\n",
1206
+ " )\n",
1207
+ " .to_df()\n",
1208
+ " .reset_index()\n",
1209
+ " .set_index(\"calendar_year\")\n",
1210
+ " .sort_index(ascending=False)[\"free_cash_flow_yield\"]\n",
1211
+ " )\n",
1212
+ "fcf_yield.transpose()"
1213
+ ]
1214
+ },
1215
+ {
1216
+ "cell_type": "markdown",
1217
+ "metadata": {},
1218
+ "source": [
1219
+ "There are more usage examples on our [website](https://docs.openbb.co/platform/user_guides)"
1220
+ ]
1221
+ }
1222
+ ],
1223
+ "metadata": {
1224
+ "kernelspec": {
1225
+ "display_name": "obb-sdk4",
1226
+ "language": "python",
1227
+ "name": "python3"
1228
+ },
1229
+ "language_info": {
1230
+ "codemirror_mode": {
1231
+ "name": "ipython",
1232
+ "version": 3
1233
+ },
1234
+ "file_extension": ".py",
1235
+ "mimetype": "text/x-python",
1236
+ "name": "python",
1237
+ "nbconvert_exporter": "python",
1238
+ "pygments_lexer": "ipython3",
1239
+ "version": "3.12.4"
1240
+ }
1241
+ },
1242
+ "nbformat": 4,
1243
+ "nbformat_minor": 2
1244
+ }
examples/financialStatements.webp ADDED
examples/findSymbols.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
examples/findSymbols.webp ADDED
examples/googleColab.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
examples/googleColab.webp ADDED
examples/impliedEarningsMove.ipynb ADDED
@@ -0,0 +1,691 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {
6
+ "id": "BzQ2PSUMb1O7"
7
+ },
8
+ "source": [
9
+ "# Calculating the Implied Earnings Move Using Options Prices\n",
10
+ "\n",
11
+ "Earnings day can be a pivotal moment for a company's share price. The confluence of expectations and reality is a tradable event, drawing crowds to the options market. Observing the surrounding action can provide insight into the consensus view on, and general sentiment of, the company.\n",
12
+ "\n",
13
+ "The cost of a straddle - the combined price of an at-the-money call and put - is a common way to gauge the near-term volatility. It's the market's expectation of the price band until expiration. While this includes time value, the isolated price of volatility will generally be higher for the expiry immediately following an earnings release.\n",
14
+ "\n",
15
+ "Have a look at companies that trade weekly options and are reporting a on Thursday. If they report after the close, the price of the one-day straddle at the bell will be the purest sample of information.\n",
16
+ "\n",
17
+ "The cells below will demonstrate how to get the data from free sources, using the OpenBB Platform."
18
+ ]
19
+ },
20
+ {
21
+ "cell_type": "code",
22
+ "execution_count": null,
23
+ "metadata": {
24
+ "id": "da3wLFHJaK1n"
25
+ },
26
+ "outputs": [],
27
+ "source": [
28
+ "# If using in Google Colab, install the OpenBB library.\n",
29
+ "\n",
30
+ "#!pip install openbb[\"all\"]\n",
31
+ "\n",
32
+ "# Restart the runtime before the next block"
33
+ ]
34
+ },
35
+ {
36
+ "cell_type": "code",
37
+ "execution_count": 7,
38
+ "metadata": {
39
+ "colab": {
40
+ "base_uri": "https://localhost:8080/"
41
+ },
42
+ "id": "7xcKh78TaTot",
43
+ "outputId": "1b00dbc7-21cd-421a-b191-b2e8685f491d"
44
+ },
45
+ "outputs": [],
46
+ "source": [
47
+ "from datetime import datetime, timedelta\n",
48
+ "\n",
49
+ "from openbb import obb\n",
50
+ "\n",
51
+ "obb.user.preferences.output_type = \"dataframe\""
52
+ ]
53
+ },
54
+ {
55
+ "cell_type": "markdown",
56
+ "metadata": {
57
+ "id": "WyKBFJg-R_r2"
58
+ },
59
+ "source": [
60
+ "If the earnings date falls on an option expiry, contracts expiring that day will not provide exposure to the after-market earnings reports."
61
+ ]
62
+ },
63
+ {
64
+ "cell_type": "code",
65
+ "execution_count": 3,
66
+ "metadata": {
67
+ "colab": {
68
+ "base_uri": "https://localhost:8080/",
69
+ "height": 711
70
+ },
71
+ "id": "49D8bfFFPEwC",
72
+ "outputId": "b2eebaaa-ef2c-456a-fca2-96f6bee41b2d"
73
+ },
74
+ "outputs": [
75
+ {
76
+ "data": {
77
+ "text/html": [
78
+ "<div>\n",
79
+ "<style scoped>\n",
80
+ " .dataframe tbody tr th:only-of-type {\n",
81
+ " vertical-align: middle;\n",
82
+ " }\n",
83
+ "\n",
84
+ " .dataframe tbody tr th {\n",
85
+ " vertical-align: top;\n",
86
+ " }\n",
87
+ "\n",
88
+ " .dataframe thead th {\n",
89
+ " text-align: right;\n",
90
+ " }\n",
91
+ "</style>\n",
92
+ "<table border=\"1\" class=\"dataframe\">\n",
93
+ " <thead>\n",
94
+ " <tr style=\"text-align: right;\">\n",
95
+ " <th></th>\n",
96
+ " <th>report_date</th>\n",
97
+ " <th>symbol</th>\n",
98
+ " <th>name</th>\n",
99
+ " <th>eps_previous</th>\n",
100
+ " <th>eps_consensus</th>\n",
101
+ " <th>num_estimates</th>\n",
102
+ " <th>period_ending</th>\n",
103
+ " <th>previous_report_date</th>\n",
104
+ " <th>reporting_time</th>\n",
105
+ " <th>market_cap</th>\n",
106
+ " </tr>\n",
107
+ " </thead>\n",
108
+ " <tbody>\n",
109
+ " <tr>\n",
110
+ " <th>166</th>\n",
111
+ " <td>2024-08-28</td>\n",
112
+ " <td>NVDA</td>\n",
113
+ " <td>NVIDIA Corporation</td>\n",
114
+ " <td>0.25</td>\n",
115
+ " <td>0.59</td>\n",
116
+ " <td>13.0</td>\n",
117
+ " <td>2024-07</td>\n",
118
+ " <td>2023-08-23</td>\n",
119
+ " <td>after-hours</td>\n",
120
+ " <td>3.160887e+12</td>\n",
121
+ " </tr>\n",
122
+ " <tr>\n",
123
+ " <th>0</th>\n",
124
+ " <td>2024-09-05</td>\n",
125
+ " <td>AVGO</td>\n",
126
+ " <td>Broadcom Inc.</td>\n",
127
+ " <td>0.95</td>\n",
128
+ " <td>0.95</td>\n",
129
+ " <td>10.0</td>\n",
130
+ " <td>2024-07</td>\n",
131
+ " <td>2023-08-31</td>\n",
132
+ " <td>after-hours</td>\n",
133
+ " <td>7.716866e+11</td>\n",
134
+ " </tr>\n",
135
+ " <tr>\n",
136
+ " <th>167</th>\n",
137
+ " <td>2024-08-28</td>\n",
138
+ " <td>CRM</td>\n",
139
+ " <td>Salesforce, Inc.</td>\n",
140
+ " <td>1.63</td>\n",
141
+ " <td>1.73</td>\n",
142
+ " <td>16.0</td>\n",
143
+ " <td>2024-07</td>\n",
144
+ " <td>2023-08-30</td>\n",
145
+ " <td>after-hours</td>\n",
146
+ " <td>2.529962e+11</td>\n",
147
+ " </tr>\n",
148
+ " <tr>\n",
149
+ " <th>261</th>\n",
150
+ " <td>2024-08-26</td>\n",
151
+ " <td>PDD</td>\n",
152
+ " <td>PDD Holdings Inc.</td>\n",
153
+ " <td>1.27</td>\n",
154
+ " <td>2.66</td>\n",
155
+ " <td>2.0</td>\n",
156
+ " <td>2024-06</td>\n",
157
+ " <td>2023-08-29</td>\n",
158
+ " <td>pre-market</td>\n",
159
+ " <td>2.007811e+11</td>\n",
160
+ " </tr>\n",
161
+ " <tr>\n",
162
+ " <th>168</th>\n",
163
+ " <td>2024-08-28</td>\n",
164
+ " <td>RY</td>\n",
165
+ " <td>Royal Bank Of Canada</td>\n",
166
+ " <td>2.13</td>\n",
167
+ " <td>2.14</td>\n",
168
+ " <td>3.0</td>\n",
169
+ " <td>2024-07</td>\n",
170
+ " <td>2023-08-24</td>\n",
171
+ " <td>pre-market</td>\n",
172
+ " <td>1.597315e+11</td>\n",
173
+ " </tr>\n",
174
+ " <tr>\n",
175
+ " <th>262</th>\n",
176
+ " <td>2024-08-26</td>\n",
177
+ " <td>BHP</td>\n",
178
+ " <td>BHP Group Limited</td>\n",
179
+ " <td>NaN</td>\n",
180
+ " <td>NaN</td>\n",
181
+ " <td>1.0</td>\n",
182
+ " <td>2024-06</td>\n",
183
+ " <td>NaN</td>\n",
184
+ " <td>after-hours</td>\n",
185
+ " <td>1.401966e+11</td>\n",
186
+ " </tr>\n",
187
+ " <tr>\n",
188
+ " <th>114</th>\n",
189
+ " <td>2024-08-29</td>\n",
190
+ " <td>DELL</td>\n",
191
+ " <td>Dell Technologies Inc.</td>\n",
192
+ " <td>1.44</td>\n",
193
+ " <td>1.49</td>\n",
194
+ " <td>4.0</td>\n",
195
+ " <td>2024-07</td>\n",
196
+ " <td>2023-08-31</td>\n",
197
+ " <td>after-hours</td>\n",
198
+ " <td>7.922927e+10</td>\n",
199
+ " </tr>\n",
200
+ " <tr>\n",
201
+ " <th>169</th>\n",
202
+ " <td>2024-08-28</td>\n",
203
+ " <td>CRWD</td>\n",
204
+ " <td>CrowdStrike Holdings, Inc.</td>\n",
205
+ " <td>0.06</td>\n",
206
+ " <td>0.23</td>\n",
207
+ " <td>14.0</td>\n",
208
+ " <td>2024-07</td>\n",
209
+ " <td>2023-08-30</td>\n",
210
+ " <td>after-hours</td>\n",
211
+ " <td>6.648856e+10</td>\n",
212
+ " </tr>\n",
213
+ " <tr>\n",
214
+ " <th>225</th>\n",
215
+ " <td>2024-08-27</td>\n",
216
+ " <td>BMO</td>\n",
217
+ " <td>Bank Of Montreal</td>\n",
218
+ " <td>2.08</td>\n",
219
+ " <td>1.98</td>\n",
220
+ " <td>3.0</td>\n",
221
+ " <td>2024-07</td>\n",
222
+ " <td>2023-08-29</td>\n",
223
+ " <td>pre-market</td>\n",
224
+ " <td>6.319707e+10</td>\n",
225
+ " </tr>\n",
226
+ " <tr>\n",
227
+ " <th>115</th>\n",
228
+ " <td>2024-08-29</td>\n",
229
+ " <td>MRVL</td>\n",
230
+ " <td>Marvell Technology, Inc.</td>\n",
231
+ " <td>0.18</td>\n",
232
+ " <td>0.13</td>\n",
233
+ " <td>13.0</td>\n",
234
+ " <td>2024-07</td>\n",
235
+ " <td>2023-08-24</td>\n",
236
+ " <td>after-hours</td>\n",
237
+ " <td>6.175190e+10</td>\n",
238
+ " </tr>\n",
239
+ " <tr>\n",
240
+ " <th>226</th>\n",
241
+ " <td>2024-08-27</td>\n",
242
+ " <td>BNS</td>\n",
243
+ " <td>Bank of Nova Scotia (The)</td>\n",
244
+ " <td>1.30</td>\n",
245
+ " <td>1.18</td>\n",
246
+ " <td>4.0</td>\n",
247
+ " <td>2024-07</td>\n",
248
+ " <td>2023-08-29</td>\n",
249
+ " <td>pre-market</td>\n",
250
+ " <td>5.855210e+10</td>\n",
251
+ " </tr>\n",
252
+ " <tr>\n",
253
+ " <th>116</th>\n",
254
+ " <td>2024-08-29</td>\n",
255
+ " <td>ADSK</td>\n",
256
+ " <td>Autodesk, Inc.</td>\n",
257
+ " <td>1.12</td>\n",
258
+ " <td>1.35</td>\n",
259
+ " <td>8.0</td>\n",
260
+ " <td>2024-07</td>\n",
261
+ " <td>2023-08-23</td>\n",
262
+ " <td>after-hours</td>\n",
263
+ " <td>5.444496e+10</td>\n",
264
+ " </tr>\n",
265
+ " <tr>\n",
266
+ " <th>117</th>\n",
267
+ " <td>2024-08-29</td>\n",
268
+ " <td>CM</td>\n",
269
+ " <td>Canadian Imperial Bank of Commerce</td>\n",
270
+ " <td>1.14</td>\n",
271
+ " <td>1.28</td>\n",
272
+ " <td>4.0</td>\n",
273
+ " <td>2024-07</td>\n",
274
+ " <td>2023-08-31</td>\n",
275
+ " <td>pre-market</td>\n",
276
+ " <td>5.042232e+10</td>\n",
277
+ " </tr>\n",
278
+ " <tr>\n",
279
+ " <th>170</th>\n",
280
+ " <td>2024-08-28</td>\n",
281
+ " <td>HPQ</td>\n",
282
+ " <td>HP Inc.</td>\n",
283
+ " <td>0.86</td>\n",
284
+ " <td>0.86</td>\n",
285
+ " <td>4.0</td>\n",
286
+ " <td>2024-07</td>\n",
287
+ " <td>2023-08-29</td>\n",
288
+ " <td>after-hours</td>\n",
289
+ " <td>3.451380e+10</td>\n",
290
+ " </tr>\n",
291
+ " <tr>\n",
292
+ " <th>227</th>\n",
293
+ " <td>2024-08-27</td>\n",
294
+ " <td>HEI</td>\n",
295
+ " <td>Heico Corporation</td>\n",
296
+ " <td>0.77</td>\n",
297
+ " <td>0.91</td>\n",
298
+ " <td>8.0</td>\n",
299
+ " <td>2024-07</td>\n",
300
+ " <td>2023-08-28</td>\n",
301
+ " <td>pre-market</td>\n",
302
+ " <td>3.387054e+10</td>\n",
303
+ " </tr>\n",
304
+ " <tr>\n",
305
+ " <th>171</th>\n",
306
+ " <td>2024-08-28</td>\n",
307
+ " <td>VEEV</td>\n",
308
+ " <td>Veeva Systems Inc.</td>\n",
309
+ " <td>0.70</td>\n",
310
+ " <td>1.04</td>\n",
311
+ " <td>10.0</td>\n",
312
+ " <td>2024-07</td>\n",
313
+ " <td>2023-08-30</td>\n",
314
+ " <td>after-hours</td>\n",
315
+ " <td>3.256312e+10</td>\n",
316
+ " </tr>\n",
317
+ " <tr>\n",
318
+ " <th>118</th>\n",
319
+ " <td>2024-08-29</td>\n",
320
+ " <td>LULU</td>\n",
321
+ " <td>lululemon athletica inc.</td>\n",
322
+ " <td>2.68</td>\n",
323
+ " <td>2.94</td>\n",
324
+ " <td>13.0</td>\n",
325
+ " <td>2024-07</td>\n",
326
+ " <td>2023-08-31</td>\n",
327
+ " <td>after-hours</td>\n",
328
+ " <td>3.184542e+10</td>\n",
329
+ " </tr>\n",
330
+ " <tr>\n",
331
+ " <th>88</th>\n",
332
+ " <td>2024-09-03</td>\n",
333
+ " <td>ZS</td>\n",
334
+ " <td>Zscaler, Inc.</td>\n",
335
+ " <td>-0.17</td>\n",
336
+ " <td>-0.14</td>\n",
337
+ " <td>12.0</td>\n",
338
+ " <td>2024-07</td>\n",
339
+ " <td>2023-09-05</td>\n",
340
+ " <td>after-hours</td>\n",
341
+ " <td>3.030086e+10</td>\n",
342
+ " </tr>\n",
343
+ " <tr>\n",
344
+ " <th>263</th>\n",
345
+ " <td>2024-08-26</td>\n",
346
+ " <td>TCOM</td>\n",
347
+ " <td>Trip.com Group Limited</td>\n",
348
+ " <td>0.60</td>\n",
349
+ " <td>0.65</td>\n",
350
+ " <td>2.0</td>\n",
351
+ " <td>2024-06</td>\n",
352
+ " <td>2023-09-04</td>\n",
353
+ " <td>after-hours</td>\n",
354
+ " <td>2.780128e+10</td>\n",
355
+ " </tr>\n",
356
+ " <tr>\n",
357
+ " <th>172</th>\n",
358
+ " <td>2024-08-28</td>\n",
359
+ " <td>NTAP</td>\n",
360
+ " <td>NetApp, Inc.</td>\n",
361
+ " <td>0.84</td>\n",
362
+ " <td>1.15</td>\n",
363
+ " <td>8.0</td>\n",
364
+ " <td>2024-07</td>\n",
365
+ " <td>2023-08-23</td>\n",
366
+ " <td>after-hours</td>\n",
367
+ " <td>2.745601e+10</td>\n",
368
+ " </tr>\n",
369
+ " </tbody>\n",
370
+ "</table>\n",
371
+ "</div>"
372
+ ],
373
+ "text/plain": [
374
+ " report_date symbol name eps_previous \\\n",
375
+ "166 2024-08-28 NVDA NVIDIA Corporation 0.25 \n",
376
+ "0 2024-09-05 AVGO Broadcom Inc. 0.95 \n",
377
+ "167 2024-08-28 CRM Salesforce, Inc. 1.63 \n",
378
+ "261 2024-08-26 PDD PDD Holdings Inc. 1.27 \n",
379
+ "168 2024-08-28 RY Royal Bank Of Canada 2.13 \n",
380
+ "262 2024-08-26 BHP BHP Group Limited NaN \n",
381
+ "114 2024-08-29 DELL Dell Technologies Inc. 1.44 \n",
382
+ "169 2024-08-28 CRWD CrowdStrike Holdings, Inc. 0.06 \n",
383
+ "225 2024-08-27 BMO Bank Of Montreal 2.08 \n",
384
+ "115 2024-08-29 MRVL Marvell Technology, Inc. 0.18 \n",
385
+ "226 2024-08-27 BNS Bank of Nova Scotia (The) 1.30 \n",
386
+ "116 2024-08-29 ADSK Autodesk, Inc. 1.12 \n",
387
+ "117 2024-08-29 CM Canadian Imperial Bank of Commerce 1.14 \n",
388
+ "170 2024-08-28 HPQ HP Inc. 0.86 \n",
389
+ "227 2024-08-27 HEI Heico Corporation 0.77 \n",
390
+ "171 2024-08-28 VEEV Veeva Systems Inc. 0.70 \n",
391
+ "118 2024-08-29 LULU lululemon athletica inc. 2.68 \n",
392
+ "88 2024-09-03 ZS Zscaler, Inc. -0.17 \n",
393
+ "263 2024-08-26 TCOM Trip.com Group Limited 0.60 \n",
394
+ "172 2024-08-28 NTAP NetApp, Inc. 0.84 \n",
395
+ "\n",
396
+ " eps_consensus num_estimates period_ending previous_report_date \\\n",
397
+ "166 0.59 13.0 2024-07 2023-08-23 \n",
398
+ "0 0.95 10.0 2024-07 2023-08-31 \n",
399
+ "167 1.73 16.0 2024-07 2023-08-30 \n",
400
+ "261 2.66 2.0 2024-06 2023-08-29 \n",
401
+ "168 2.14 3.0 2024-07 2023-08-24 \n",
402
+ "262 NaN 1.0 2024-06 NaN \n",
403
+ "114 1.49 4.0 2024-07 2023-08-31 \n",
404
+ "169 0.23 14.0 2024-07 2023-08-30 \n",
405
+ "225 1.98 3.0 2024-07 2023-08-29 \n",
406
+ "115 0.13 13.0 2024-07 2023-08-24 \n",
407
+ "226 1.18 4.0 2024-07 2023-08-29 \n",
408
+ "116 1.35 8.0 2024-07 2023-08-23 \n",
409
+ "117 1.28 4.0 2024-07 2023-08-31 \n",
410
+ "170 0.86 4.0 2024-07 2023-08-29 \n",
411
+ "227 0.91 8.0 2024-07 2023-08-28 \n",
412
+ "171 1.04 10.0 2024-07 2023-08-30 \n",
413
+ "118 2.94 13.0 2024-07 2023-08-31 \n",
414
+ "88 -0.14 12.0 2024-07 2023-09-05 \n",
415
+ "263 0.65 2.0 2024-06 2023-09-04 \n",
416
+ "172 1.15 8.0 2024-07 2023-08-23 \n",
417
+ "\n",
418
+ " reporting_time market_cap \n",
419
+ "166 after-hours 3.160887e+12 \n",
420
+ "0 after-hours 7.716866e+11 \n",
421
+ "167 after-hours 2.529962e+11 \n",
422
+ "261 pre-market 2.007811e+11 \n",
423
+ "168 pre-market 1.597315e+11 \n",
424
+ "262 after-hours 1.401966e+11 \n",
425
+ "114 after-hours 7.922927e+10 \n",
426
+ "169 after-hours 6.648856e+10 \n",
427
+ "225 pre-market 6.319707e+10 \n",
428
+ "115 after-hours 6.175190e+10 \n",
429
+ "226 pre-market 5.855210e+10 \n",
430
+ "116 after-hours 5.444496e+10 \n",
431
+ "117 pre-market 5.042232e+10 \n",
432
+ "170 after-hours 3.451380e+10 \n",
433
+ "227 pre-market 3.387054e+10 \n",
434
+ "171 after-hours 3.256312e+10 \n",
435
+ "118 after-hours 3.184542e+10 \n",
436
+ "88 after-hours 3.030086e+10 \n",
437
+ "263 after-hours 2.780128e+10 \n",
438
+ "172 after-hours 2.745601e+10 "
439
+ ]
440
+ },
441
+ "execution_count": 3,
442
+ "metadata": {},
443
+ "output_type": "execute_result"
444
+ }
445
+ ],
446
+ "source": [
447
+ "# Lookup some upcoming earnings dates and sort them by market cap.\n",
448
+ "\n",
449
+ "earnings_calendar = obb.equity.calendar.earnings(\n",
450
+ " start_date=(datetime.now() + timedelta(days=1)).date(),\n",
451
+ " end_date=(datetime.now() + timedelta(days=14)).date(),\n",
452
+ " provider=\"nasdaq\",\n",
453
+ ")\n",
454
+ "\n",
455
+ "earnings_calendar.sort_values(by=[\"market_cap\", \"num_estimates\"], ascending=False).head(\n",
456
+ " 20\n",
457
+ ")"
458
+ ]
459
+ },
460
+ {
461
+ "cell_type": "code",
462
+ "execution_count": 27,
463
+ "metadata": {
464
+ "colab": {
465
+ "base_uri": "https://localhost:8080/"
466
+ },
467
+ "id": "IzmLloIfTQJo",
468
+ "outputId": "1c08fa80-eaf2-4548-8c27-70c82fac79ea"
469
+ },
470
+ "outputs": [
471
+ {
472
+ "data": {
473
+ "text/plain": [
474
+ "'Last Price: $124.7001'"
475
+ ]
476
+ },
477
+ "metadata": {},
478
+ "output_type": "display_data"
479
+ },
480
+ {
481
+ "data": {
482
+ "text/plain": [
483
+ "['2024-08-23', '2024-08-30', '2024-09-06']"
484
+ ]
485
+ },
486
+ "metadata": {},
487
+ "output_type": "display_data"
488
+ }
489
+ ],
490
+ "source": [
491
+ "# Get the options chains data.\n",
492
+ "\n",
493
+ "symbol = \"NVDA\" # This will not be evergreen, change the symbol based on a stock above.\n",
494
+ "\n",
495
+ "obb.user.preferences.output_type = \"OBBject\" # To use the built-in options chains methods, we need to set the output type to OBBject.\n",
496
+ "\n",
497
+ "options = obb.derivatives.options.chains(symbol, provider=\"cboe\")\n",
498
+ "\n",
499
+ "last_price = options.results.underlying_price[0]\n",
500
+ "\n",
501
+ "display(f\"Last Price: ${last_price}\")\n",
502
+ "\n",
503
+ "display(options.results.expirations[:3])"
504
+ ]
505
+ },
506
+ {
507
+ "cell_type": "code",
508
+ "execution_count": 40,
509
+ "metadata": {},
510
+ "outputs": [
511
+ {
512
+ "name": "stdout",
513
+ "output_type": "stream",
514
+ "text": [
515
+ "Cost of Straddle: $14.65\n",
516
+ "Cost as a % of Share Price: 11.7482%\n",
517
+ "Upper Breakeven Price: $139.65\n",
518
+ "Lower Breakeven Price: $109.35\n",
519
+ "Implied Daily Move: 1.3982%\n",
520
+ "\n"
521
+ ]
522
+ },
523
+ {
524
+ "data": {
525
+ "text/html": [
526
+ "<div>\n",
527
+ "<style scoped>\n",
528
+ " .dataframe tbody tr th:only-of-type {\n",
529
+ " vertical-align: middle;\n",
530
+ " }\n",
531
+ "\n",
532
+ " .dataframe tbody tr th {\n",
533
+ " vertical-align: top;\n",
534
+ " }\n",
535
+ "\n",
536
+ " .dataframe thead th {\n",
537
+ " text-align: right;\n",
538
+ " }\n",
539
+ "</style>\n",
540
+ "<table border=\"1\" class=\"dataframe\">\n",
541
+ " <thead>\n",
542
+ " <tr style=\"text-align: right;\">\n",
543
+ " <th></th>\n",
544
+ " <th>Long Straddle</th>\n",
545
+ " </tr>\n",
546
+ " </thead>\n",
547
+ " <tbody>\n",
548
+ " <tr>\n",
549
+ " <th>Symbol</th>\n",
550
+ " <td>NVDA</td>\n",
551
+ " </tr>\n",
552
+ " <tr>\n",
553
+ " <th>Underlying Price</th>\n",
554
+ " <td>124.7001</td>\n",
555
+ " </tr>\n",
556
+ " <tr>\n",
557
+ " <th>Expiration</th>\n",
558
+ " <td>2024-08-30</td>\n",
559
+ " </tr>\n",
560
+ " <tr>\n",
561
+ " <th>DTE</th>\n",
562
+ " <td>8</td>\n",
563
+ " </tr>\n",
564
+ " <tr>\n",
565
+ " <th>Strike 1</th>\n",
566
+ " <td>125.0</td>\n",
567
+ " </tr>\n",
568
+ " <tr>\n",
569
+ " <th>Strike 2</th>\n",
570
+ " <td>124.0</td>\n",
571
+ " </tr>\n",
572
+ " <tr>\n",
573
+ " <th>Strike 1 Premium</th>\n",
574
+ " <td>7.55</td>\n",
575
+ " </tr>\n",
576
+ " <tr>\n",
577
+ " <th>Strike 2 Premium</th>\n",
578
+ " <td>7.1</td>\n",
579
+ " </tr>\n",
580
+ " <tr>\n",
581
+ " <th>Cost</th>\n",
582
+ " <td>14.65</td>\n",
583
+ " </tr>\n",
584
+ " <tr>\n",
585
+ " <th>Cost Percent</th>\n",
586
+ " <td>11.7482</td>\n",
587
+ " </tr>\n",
588
+ " <tr>\n",
589
+ " <th>Breakeven Upper</th>\n",
590
+ " <td>139.65</td>\n",
591
+ " </tr>\n",
592
+ " <tr>\n",
593
+ " <th>Breakeven Upper Percent</th>\n",
594
+ " <td>11.9887</td>\n",
595
+ " </tr>\n",
596
+ " <tr>\n",
597
+ " <th>Breakeven Lower</th>\n",
598
+ " <td>109.35</td>\n",
599
+ " </tr>\n",
600
+ " <tr>\n",
601
+ " <th>Breakeven Lower Percent</th>\n",
602
+ " <td>-12.3096</td>\n",
603
+ " </tr>\n",
604
+ " <tr>\n",
605
+ " <th>Max Profit</th>\n",
606
+ " <td>inf</td>\n",
607
+ " </tr>\n",
608
+ " <tr>\n",
609
+ " <th>Max Loss</th>\n",
610
+ " <td>-14.65</td>\n",
611
+ " </tr>\n",
612
+ " <tr>\n",
613
+ " <th>Payoff Ratio</th>\n",
614
+ " <td>inf</td>\n",
615
+ " </tr>\n",
616
+ " </tbody>\n",
617
+ "</table>\n",
618
+ "</div>"
619
+ ],
620
+ "text/plain": [
621
+ " Long Straddle\n",
622
+ "Symbol NVDA\n",
623
+ "Underlying Price 124.7001\n",
624
+ "Expiration 2024-08-30\n",
625
+ "DTE 8\n",
626
+ "Strike 1 125.0\n",
627
+ "Strike 2 124.0\n",
628
+ "Strike 1 Premium 7.55\n",
629
+ "Strike 2 Premium 7.1\n",
630
+ "Cost 14.65\n",
631
+ "Cost Percent 11.7482\n",
632
+ "Breakeven Upper 139.65\n",
633
+ "Breakeven Upper Percent 11.9887\n",
634
+ "Breakeven Lower 109.35\n",
635
+ "Breakeven Lower Percent -12.3096\n",
636
+ "Max Profit inf\n",
637
+ "Max Loss -14.65\n",
638
+ "Payoff Ratio inf"
639
+ ]
640
+ },
641
+ "metadata": {},
642
+ "output_type": "display_data"
643
+ }
644
+ ],
645
+ "source": [
646
+ "# Use the straddle method of the results object to get the straddle data and then calculate the implied move.\n",
647
+ "\n",
648
+ "straddle = options.results.straddle(days=options.results.expirations[1])\n",
649
+ "straddle_price = straddle.loc[\"Cost\"].values[0]\n",
650
+ "days = straddle.loc[\"DTE\"].values[0]\n",
651
+ "upper_price = straddle.loc[\"Breakeven Upper\"].values[0]\n",
652
+ "lower_price = straddle.loc[\"Breakeven Lower\"].values[0]\n",
653
+ "\n",
654
+ "implied_move = ((1 + straddle_price / last_price) ** (1 / days) - 1) * 100\n",
655
+ "\n",
656
+ "display(\n",
657
+ " f\"Cost of Straddle: ${round(straddle_price, 2)}\"\n",
658
+ " f\"\\nCost as a % of Share Price: {round((straddle_price/last_price) * 100, 4)}%\"\n",
659
+ " f\"\\nUpper Breakeven Price: ${upper_price}\"\n",
660
+ " f\"\\nLower Breakeven Price: ${lower_price}\"\n",
661
+ " f\"\\nImplied Daily Move: {round(implied_move, 4)}%\\n\"\n",
662
+ ")\n",
663
+ "\n",
664
+ "display(straddle)"
665
+ ]
666
+ }
667
+ ],
668
+ "metadata": {
669
+ "colab": {
670
+ "provenance": []
671
+ },
672
+ "kernelspec": {
673
+ "display_name": "Python 3",
674
+ "name": "python3"
675
+ },
676
+ "language_info": {
677
+ "codemirror_mode": {
678
+ "name": "ipython",
679
+ "version": 3
680
+ },
681
+ "file_extension": ".py",
682
+ "mimetype": "text/x-python",
683
+ "name": "python",
684
+ "nbconvert_exporter": "python",
685
+ "pygments_lexer": "ipython3",
686
+ "version": "3.12.4"
687
+ }
688
+ },
689
+ "nbformat": 4,
690
+ "nbformat_minor": 0
691
+ }
examples/impliedEarningsMove.webp ADDED
examples/loadHistoricalPriceData.ipynb ADDED
@@ -0,0 +1,2514 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "attachments": {},
5
+ "cell_type": "markdown",
6
+ "metadata": {},
7
+ "source": [
8
+ "# Historical Prices With the OpenBB Platform\n",
9
+ "\n",
10
+ "This notebook demonstrates some of the ways to approach loading historical price data using the OpenBB Platform. The action is in the Equity module; but first, we need to initialize the notebook with the import statements block.\n",
11
+ "\n",
12
+ "## Import Statements"
13
+ ]
14
+ },
15
+ {
16
+ "cell_type": "code",
17
+ "execution_count": 1,
18
+ "metadata": {},
19
+ "outputs": [],
20
+ "source": [
21
+ "from datetime import datetime, timedelta\n",
22
+ "\n",
23
+ "import pandas as pd\n",
24
+ "from openbb import obb"
25
+ ]
26
+ },
27
+ {
28
+ "attachments": {},
29
+ "cell_type": "markdown",
30
+ "metadata": {},
31
+ "source": [
32
+ "## The Equity Module\n",
33
+ "\n",
34
+ "Historical market prices typically come in the form of OHLC+V - open, high, low, close, volume. There may be additional fields returned by a provider, but those are the expected columns. Granularity and amount of historical data will vary by provider and subscription status. Visit their websites to understand what your entitlements are.\n",
35
+ "\n",
36
+ "### openbb.equity.price.historical()\n",
37
+ "\n",
38
+ "- This endpoint has the most number of providers out of any function. At the time of writing, choices are:\n",
39
+ "\n",
40
+ "['alpha_vantage', 'cboe', 'fmp', 'intrinio', 'polygon', 'tiingo', 'yfinance']\n",
41
+ "\n",
42
+ "- Common parameters have been standardized across all souces, `start_date`, `end_date`, `interval`.\n",
43
+ "\n",
44
+ "- The default interval will be `1d`.\n",
45
+ "\n",
46
+ "- The depth of historical data and choices for granularity will vary by provider and subscription status. Refer to the website and documentation of each source understand your specific entitlements.\n",
47
+ "\n",
48
+ "- For demonstration purposes, we will use the `openbb-yfinance` data extension."
49
+ ]
50
+ },
51
+ {
52
+ "cell_type": "code",
53
+ "execution_count": 2,
54
+ "metadata": {},
55
+ "outputs": [
56
+ {
57
+ "data": {
58
+ "text/html": [
59
+ "<div>\n",
60
+ "<style scoped>\n",
61
+ " .dataframe tbody tr th:only-of-type {\n",
62
+ " vertical-align: middle;\n",
63
+ " }\n",
64
+ "\n",
65
+ " .dataframe tbody tr th {\n",
66
+ " vertical-align: top;\n",
67
+ " }\n",
68
+ "\n",
69
+ " .dataframe thead th {\n",
70
+ " text-align: right;\n",
71
+ " }\n",
72
+ "</style>\n",
73
+ "<table border=\"1\" class=\"dataframe\">\n",
74
+ " <thead>\n",
75
+ " <tr style=\"text-align: right;\">\n",
76
+ " <th></th>\n",
77
+ " <th>open</th>\n",
78
+ " <th>high</th>\n",
79
+ " <th>low</th>\n",
80
+ " <th>close</th>\n",
81
+ " <th>volume</th>\n",
82
+ " <th>split_ratio</th>\n",
83
+ " <th>dividend</th>\n",
84
+ " <th>capital_gains</th>\n",
85
+ " </tr>\n",
86
+ " <tr>\n",
87
+ " <th>date</th>\n",
88
+ " <th></th>\n",
89
+ " <th></th>\n",
90
+ " <th></th>\n",
91
+ " <th></th>\n",
92
+ " <th></th>\n",
93
+ " <th></th>\n",
94
+ " <th></th>\n",
95
+ " <th></th>\n",
96
+ " </tr>\n",
97
+ " </thead>\n",
98
+ " <tbody>\n",
99
+ " <tr>\n",
100
+ " <th>2023-08-22</th>\n",
101
+ " <td>441.179993</td>\n",
102
+ " <td>441.179993</td>\n",
103
+ " <td>437.570007</td>\n",
104
+ " <td>438.149994</td>\n",
105
+ " <td>65062900</td>\n",
106
+ " <td>0.0</td>\n",
107
+ " <td>0.0</td>\n",
108
+ " <td>0.0</td>\n",
109
+ " </tr>\n",
110
+ " </tbody>\n",
111
+ "</table>\n",
112
+ "</div>"
113
+ ],
114
+ "text/plain": [
115
+ " open high low close volume \\\n",
116
+ "date \n",
117
+ "2023-08-22 441.179993 441.179993 437.570007 438.149994 65062900 \n",
118
+ "\n",
119
+ " split_ratio dividend capital_gains \n",
120
+ "date \n",
121
+ "2023-08-22 0.0 0.0 0.0 "
122
+ ]
123
+ },
124
+ "execution_count": 2,
125
+ "metadata": {},
126
+ "output_type": "execute_result"
127
+ }
128
+ ],
129
+ "source": [
130
+ "df_daily = obb.equity.price.historical(symbol=\"spy\", provider=\"yfinance\")\n",
131
+ "df_daily.to_df().head(1)"
132
+ ]
133
+ },
134
+ {
135
+ "attachments": {},
136
+ "cell_type": "markdown",
137
+ "metadata": {},
138
+ "source": [
139
+ "To load the entire history available from a source, pick a starting date well beyond what it might be. For example, `1900-01-01`"
140
+ ]
141
+ },
142
+ {
143
+ "cell_type": "code",
144
+ "execution_count": 3,
145
+ "metadata": {},
146
+ "outputs": [
147
+ {
148
+ "data": {
149
+ "text/html": [
150
+ "<div>\n",
151
+ "<style scoped>\n",
152
+ " .dataframe tbody tr th:only-of-type {\n",
153
+ " vertical-align: middle;\n",
154
+ " }\n",
155
+ "\n",
156
+ " .dataframe tbody tr th {\n",
157
+ " vertical-align: top;\n",
158
+ " }\n",
159
+ "\n",
160
+ " .dataframe thead th {\n",
161
+ " text-align: right;\n",
162
+ " }\n",
163
+ "</style>\n",
164
+ "<table border=\"1\" class=\"dataframe\">\n",
165
+ " <thead>\n",
166
+ " <tr style=\"text-align: right;\">\n",
167
+ " <th></th>\n",
168
+ " <th>open</th>\n",
169
+ " <th>high</th>\n",
170
+ " <th>low</th>\n",
171
+ " <th>close</th>\n",
172
+ " <th>volume</th>\n",
173
+ " <th>split_ratio</th>\n",
174
+ " <th>dividend</th>\n",
175
+ " <th>capital_gains</th>\n",
176
+ " </tr>\n",
177
+ " <tr>\n",
178
+ " <th>date</th>\n",
179
+ " <th></th>\n",
180
+ " <th></th>\n",
181
+ " <th></th>\n",
182
+ " <th></th>\n",
183
+ " <th></th>\n",
184
+ " <th></th>\n",
185
+ " <th></th>\n",
186
+ " <th></th>\n",
187
+ " </tr>\n",
188
+ " </thead>\n",
189
+ " <tbody>\n",
190
+ " <tr>\n",
191
+ " <th>1993-01-29</th>\n",
192
+ " <td>43.96875</td>\n",
193
+ " <td>43.96875</td>\n",
194
+ " <td>43.75</td>\n",
195
+ " <td>43.9375</td>\n",
196
+ " <td>1003200</td>\n",
197
+ " <td>0.0</td>\n",
198
+ " <td>0.0</td>\n",
199
+ " <td>0.0</td>\n",
200
+ " </tr>\n",
201
+ " </tbody>\n",
202
+ "</table>\n",
203
+ "</div>"
204
+ ],
205
+ "text/plain": [
206
+ " open high low close volume split_ratio \\\n",
207
+ "date \n",
208
+ "1993-01-29 43.96875 43.96875 43.75 43.9375 1003200 0.0 \n",
209
+ "\n",
210
+ " dividend capital_gains \n",
211
+ "date \n",
212
+ "1993-01-29 0.0 0.0 "
213
+ ]
214
+ },
215
+ "execution_count": 3,
216
+ "metadata": {},
217
+ "output_type": "execute_result"
218
+ }
219
+ ],
220
+ "source": [
221
+ "df_daily = obb.equity.price.historical(\n",
222
+ " symbol=\"spy\", start_date=\"1990-01-01\", provider=\"yfinance\"\n",
223
+ ").to_df()\n",
224
+ "df_daily.head(1)"
225
+ ]
226
+ },
227
+ {
228
+ "cell_type": "markdown",
229
+ "metadata": {},
230
+ "source": [
231
+ "#### Intervals\n",
232
+ "\n",
233
+ "The intervals are entered according to this pattern:\n",
234
+ "\n",
235
+ "- `1m` = One Minute\n",
236
+ "- `1h` = One Hour\n",
237
+ "- `1d` = One Day\n",
238
+ "- `1W` = One Week\n",
239
+ "- `1M` = One Month\n",
240
+ "\n",
241
+ "The date for monthly value is the first or last, depending on the provider. This can be easily resampled from daily data."
242
+ ]
243
+ },
244
+ {
245
+ "cell_type": "code",
246
+ "execution_count": 4,
247
+ "metadata": {},
248
+ "outputs": [
249
+ {
250
+ "data": {
251
+ "text/html": [
252
+ "<div>\n",
253
+ "<style scoped>\n",
254
+ " .dataframe tbody tr th:only-of-type {\n",
255
+ " vertical-align: middle;\n",
256
+ " }\n",
257
+ "\n",
258
+ " .dataframe tbody tr th {\n",
259
+ " vertical-align: top;\n",
260
+ " }\n",
261
+ "\n",
262
+ " .dataframe thead th {\n",
263
+ " text-align: right;\n",
264
+ " }\n",
265
+ "</style>\n",
266
+ "<table border=\"1\" class=\"dataframe\">\n",
267
+ " <thead>\n",
268
+ " <tr style=\"text-align: right;\">\n",
269
+ " <th></th>\n",
270
+ " <th>open</th>\n",
271
+ " <th>high</th>\n",
272
+ " <th>low</th>\n",
273
+ " <th>close</th>\n",
274
+ " <th>volume</th>\n",
275
+ " <th>split_ratio</th>\n",
276
+ " <th>dividend</th>\n",
277
+ " <th>capital_gains</th>\n",
278
+ " </tr>\n",
279
+ " <tr>\n",
280
+ " <th>date</th>\n",
281
+ " <th></th>\n",
282
+ " <th></th>\n",
283
+ " <th></th>\n",
284
+ " <th></th>\n",
285
+ " <th></th>\n",
286
+ " <th></th>\n",
287
+ " <th></th>\n",
288
+ " <th></th>\n",
289
+ " </tr>\n",
290
+ " </thead>\n",
291
+ " <tbody>\n",
292
+ " <tr>\n",
293
+ " <th>2024-07-01</th>\n",
294
+ " <td>545.630005</td>\n",
295
+ " <td>565.159973</td>\n",
296
+ " <td>537.450012</td>\n",
297
+ " <td>550.809998</td>\n",
298
+ " <td>1038465500</td>\n",
299
+ " <td>0.0</td>\n",
300
+ " <td>0.0</td>\n",
301
+ " <td>0.0</td>\n",
302
+ " </tr>\n",
303
+ " <tr>\n",
304
+ " <th>2024-08-01</th>\n",
305
+ " <td>552.570007</td>\n",
306
+ " <td>563.150024</td>\n",
307
+ " <td>510.269989</td>\n",
308
+ " <td>556.570007</td>\n",
309
+ " <td>954486073</td>\n",
310
+ " <td>0.0</td>\n",
311
+ " <td>0.0</td>\n",
312
+ " <td>0.0</td>\n",
313
+ " </tr>\n",
314
+ " </tbody>\n",
315
+ "</table>\n",
316
+ "</div>"
317
+ ],
318
+ "text/plain": [
319
+ " open high low close volume \\\n",
320
+ "date \n",
321
+ "2024-07-01 545.630005 565.159973 537.450012 550.809998 1038465500 \n",
322
+ "2024-08-01 552.570007 563.150024 510.269989 556.570007 954486073 \n",
323
+ "\n",
324
+ " split_ratio dividend capital_gains \n",
325
+ "date \n",
326
+ "2024-07-01 0.0 0.0 0.0 \n",
327
+ "2024-08-01 0.0 0.0 0.0 "
328
+ ]
329
+ },
330
+ "execution_count": 4,
331
+ "metadata": {},
332
+ "output_type": "execute_result"
333
+ }
334
+ ],
335
+ "source": [
336
+ "df_monthly = obb.equity.price.historical(\n",
337
+ " \"spy\", start_date=\"1990-01-01\", interval=\"1M\", provider=\"yfinance\"\n",
338
+ ").to_df()\n",
339
+ "df_monthly.tail(2)"
340
+ ]
341
+ },
342
+ {
343
+ "cell_type": "markdown",
344
+ "metadata": {},
345
+ "source": [
346
+ "#### Resample a Time Series\n",
347
+ "\n",
348
+ "`yfinance` returns the monthly data for the first day of each month. Let's resample it to take from the last, using the daily information captured in the previous cells."
349
+ ]
350
+ },
351
+ {
352
+ "cell_type": "code",
353
+ "execution_count": 6,
354
+ "metadata": {},
355
+ "outputs": [
356
+ {
357
+ "data": {
358
+ "text/html": [
359
+ "<div>\n",
360
+ "<style scoped>\n",
361
+ " .dataframe tbody tr th:only-of-type {\n",
362
+ " vertical-align: middle;\n",
363
+ " }\n",
364
+ "\n",
365
+ " .dataframe tbody tr th {\n",
366
+ " vertical-align: top;\n",
367
+ " }\n",
368
+ "\n",
369
+ " .dataframe thead th {\n",
370
+ " text-align: right;\n",
371
+ " }\n",
372
+ "</style>\n",
373
+ "<table border=\"1\" class=\"dataframe\">\n",
374
+ " <thead>\n",
375
+ " <tr style=\"text-align: right;\">\n",
376
+ " <th></th>\n",
377
+ " <th>open</th>\n",
378
+ " <th>high</th>\n",
379
+ " <th>low</th>\n",
380
+ " <th>close</th>\n",
381
+ " <th>volume</th>\n",
382
+ " </tr>\n",
383
+ " <tr>\n",
384
+ " <th>date</th>\n",
385
+ " <th></th>\n",
386
+ " <th></th>\n",
387
+ " <th></th>\n",
388
+ " <th></th>\n",
389
+ " <th></th>\n",
390
+ " </tr>\n",
391
+ " </thead>\n",
392
+ " <tbody>\n",
393
+ " <tr>\n",
394
+ " <th>1993-01-31</th>\n",
395
+ " <td>43.968750</td>\n",
396
+ " <td>43.968750</td>\n",
397
+ " <td>43.750000</td>\n",
398
+ " <td>43.937500</td>\n",
399
+ " <td>1003200</td>\n",
400
+ " </tr>\n",
401
+ " <tr>\n",
402
+ " <th>1993-02-28</th>\n",
403
+ " <td>43.968750</td>\n",
404
+ " <td>45.125000</td>\n",
405
+ " <td>42.812500</td>\n",
406
+ " <td>44.406250</td>\n",
407
+ " <td>5417600</td>\n",
408
+ " </tr>\n",
409
+ " <tr>\n",
410
+ " <th>1993-03-31</th>\n",
411
+ " <td>44.562500</td>\n",
412
+ " <td>45.843750</td>\n",
413
+ " <td>44.218750</td>\n",
414
+ " <td>45.187500</td>\n",
415
+ " <td>3019200</td>\n",
416
+ " </tr>\n",
417
+ " <tr>\n",
418
+ " <th>1993-04-30</th>\n",
419
+ " <td>45.250000</td>\n",
420
+ " <td>45.250000</td>\n",
421
+ " <td>43.281250</td>\n",
422
+ " <td>44.031250</td>\n",
423
+ " <td>2697200</td>\n",
424
+ " </tr>\n",
425
+ " <tr>\n",
426
+ " <th>1993-05-31</th>\n",
427
+ " <td>44.093750</td>\n",
428
+ " <td>45.656250</td>\n",
429
+ " <td>43.843750</td>\n",
430
+ " <td>45.218750</td>\n",
431
+ " <td>1808000</td>\n",
432
+ " </tr>\n",
433
+ " <tr>\n",
434
+ " <th>...</th>\n",
435
+ " <td>...</td>\n",
436
+ " <td>...</td>\n",
437
+ " <td>...</td>\n",
438
+ " <td>...</td>\n",
439
+ " <td>...</td>\n",
440
+ " </tr>\n",
441
+ " <tr>\n",
442
+ " <th>2024-04-30</th>\n",
443
+ " <td>523.830017</td>\n",
444
+ " <td>524.380005</td>\n",
445
+ " <td>493.859985</td>\n",
446
+ " <td>501.980011</td>\n",
447
+ " <td>1592974000</td>\n",
448
+ " </tr>\n",
449
+ " <tr>\n",
450
+ " <th>2024-05-31</th>\n",
451
+ " <td>501.380005</td>\n",
452
+ " <td>533.070007</td>\n",
453
+ " <td>499.549988</td>\n",
454
+ " <td>527.369995</td>\n",
455
+ " <td>1153264400</td>\n",
456
+ " </tr>\n",
457
+ " <tr>\n",
458
+ " <th>2024-06-30</th>\n",
459
+ " <td>529.020020</td>\n",
460
+ " <td>550.280029</td>\n",
461
+ " <td>522.599976</td>\n",
462
+ " <td>544.219971</td>\n",
463
+ " <td>888923200</td>\n",
464
+ " </tr>\n",
465
+ " <tr>\n",
466
+ " <th>2024-07-31</th>\n",
467
+ " <td>545.630005</td>\n",
468
+ " <td>565.159973</td>\n",
469
+ " <td>537.450012</td>\n",
470
+ " <td>550.809998</td>\n",
471
+ " <td>1038465500</td>\n",
472
+ " </tr>\n",
473
+ " <tr>\n",
474
+ " <th>2024-08-31</th>\n",
475
+ " <td>552.570007</td>\n",
476
+ " <td>563.150024</td>\n",
477
+ " <td>510.269989</td>\n",
478
+ " <td>556.565002</td>\n",
479
+ " <td>954484078</td>\n",
480
+ " </tr>\n",
481
+ " </tbody>\n",
482
+ "</table>\n",
483
+ "<p>380 rows × 5 columns</p>\n",
484
+ "</div>"
485
+ ],
486
+ "text/plain": [
487
+ " open high low close volume\n",
488
+ "date \n",
489
+ "1993-01-31 43.968750 43.968750 43.750000 43.937500 1003200\n",
490
+ "1993-02-28 43.968750 45.125000 42.812500 44.406250 5417600\n",
491
+ "1993-03-31 44.562500 45.843750 44.218750 45.187500 3019200\n",
492
+ "1993-04-30 45.250000 45.250000 43.281250 44.031250 2697200\n",
493
+ "1993-05-31 44.093750 45.656250 43.843750 45.218750 1808000\n",
494
+ "... ... ... ... ... ...\n",
495
+ "2024-04-30 523.830017 524.380005 493.859985 501.980011 1592974000\n",
496
+ "2024-05-31 501.380005 533.070007 499.549988 527.369995 1153264400\n",
497
+ "2024-06-30 529.020020 550.280029 522.599976 544.219971 888923200\n",
498
+ "2024-07-31 545.630005 565.159973 537.450012 550.809998 1038465500\n",
499
+ "2024-08-31 552.570007 563.150024 510.269989 556.565002 954484078\n",
500
+ "\n",
501
+ "[380 rows x 5 columns]"
502
+ ]
503
+ },
504
+ "execution_count": 6,
505
+ "metadata": {},
506
+ "output_type": "execute_result"
507
+ }
508
+ ],
509
+ "source": [
510
+ "df_daily.index = pd.to_datetime(df_daily.index)\n",
511
+ "(\n",
512
+ " df_daily[[\"open\", \"high\", \"low\", \"close\", \"volume\"]]\n",
513
+ " .resample(\"ME\")\n",
514
+ " .agg(\n",
515
+ " {\"open\": \"first\", \"high\": \"max\", \"low\": \"min\", \"close\": \"last\", \"volume\": \"sum\"}\n",
516
+ " )\n",
517
+ ")"
518
+ ]
519
+ },
520
+ {
521
+ "attachments": {},
522
+ "cell_type": "markdown",
523
+ "metadata": {},
524
+ "source": [
525
+ "The block below packs an object with most intervals."
526
+ ]
527
+ },
528
+ {
529
+ "cell_type": "code",
530
+ "execution_count": 8,
531
+ "metadata": {},
532
+ "outputs": [
533
+ {
534
+ "data": {
535
+ "text/plain": [
536
+ "dict_keys(['one', 'five', 'fifteen', 'thirty', 'sixty', 'daily', 'weekly', 'monthly'])"
537
+ ]
538
+ },
539
+ "metadata": {},
540
+ "output_type": "display_data"
541
+ },
542
+ {
543
+ "data": {
544
+ "text/html": [
545
+ "<div>\n",
546
+ "<style scoped>\n",
547
+ " .dataframe tbody tr th:only-of-type {\n",
548
+ " vertical-align: middle;\n",
549
+ " }\n",
550
+ "\n",
551
+ " .dataframe tbody tr th {\n",
552
+ " vertical-align: top;\n",
553
+ " }\n",
554
+ "\n",
555
+ " .dataframe thead th {\n",
556
+ " text-align: right;\n",
557
+ " }\n",
558
+ "</style>\n",
559
+ "<table border=\"1\" class=\"dataframe\">\n",
560
+ " <thead>\n",
561
+ " <tr style=\"text-align: right;\">\n",
562
+ " <th></th>\n",
563
+ " <th>open</th>\n",
564
+ " <th>high</th>\n",
565
+ " <th>low</th>\n",
566
+ " <th>close</th>\n",
567
+ " <th>volume</th>\n",
568
+ " <th>split_ratio</th>\n",
569
+ " <th>dividend</th>\n",
570
+ " <th>capital_gains</th>\n",
571
+ " </tr>\n",
572
+ " <tr>\n",
573
+ " <th>date</th>\n",
574
+ " <th></th>\n",
575
+ " <th></th>\n",
576
+ " <th></th>\n",
577
+ " <th></th>\n",
578
+ " <th></th>\n",
579
+ " <th></th>\n",
580
+ " <th></th>\n",
581
+ " <th></th>\n",
582
+ " </tr>\n",
583
+ " </thead>\n",
584
+ " <tbody>\n",
585
+ " <tr>\n",
586
+ " <th>2024-08-12</th>\n",
587
+ " <td>534.210022</td>\n",
588
+ " <td>555.02002</td>\n",
589
+ " <td>530.950012</td>\n",
590
+ " <td>554.309998</td>\n",
591
+ " <td>242599600</td>\n",
592
+ " <td>0</td>\n",
593
+ " <td>0.0</td>\n",
594
+ " <td>0</td>\n",
595
+ " </tr>\n",
596
+ " <tr>\n",
597
+ " <th>2024-08-19</th>\n",
598
+ " <td>554.72998</td>\n",
599
+ " <td>563.150024</td>\n",
600
+ " <td>553.859985</td>\n",
601
+ " <td>557.031006</td>\n",
602
+ " <td>142159243</td>\n",
603
+ " <td>0</td>\n",
604
+ " <td>0.0</td>\n",
605
+ " <td>0</td>\n",
606
+ " </tr>\n",
607
+ " </tbody>\n",
608
+ "</table>\n",
609
+ "</div>"
610
+ ],
611
+ "text/plain": [
612
+ " open high low close volume \\\n",
613
+ "date \n",
614
+ "2024-08-12 534.210022 555.02002 530.950012 554.309998 242599600 \n",
615
+ "2024-08-19 554.72998 563.150024 553.859985 557.031006 142159243 \n",
616
+ "\n",
617
+ " split_ratio dividend capital_gains \n",
618
+ "date \n",
619
+ "2024-08-12 0 0.0 0 \n",
620
+ "2024-08-19 0 0.0 0 "
621
+ ]
622
+ },
623
+ "metadata": {},
624
+ "output_type": "display_data"
625
+ },
626
+ {
627
+ "data": {
628
+ "text/html": [
629
+ "<div>\n",
630
+ "<style scoped>\n",
631
+ " .dataframe tbody tr th:only-of-type {\n",
632
+ " vertical-align: middle;\n",
633
+ " }\n",
634
+ "\n",
635
+ " .dataframe tbody tr th {\n",
636
+ " vertical-align: top;\n",
637
+ " }\n",
638
+ "\n",
639
+ " .dataframe thead th {\n",
640
+ " text-align: right;\n",
641
+ " }\n",
642
+ "</style>\n",
643
+ "<table border=\"1\" class=\"dataframe\">\n",
644
+ " <thead>\n",
645
+ " <tr style=\"text-align: right;\">\n",
646
+ " <th></th>\n",
647
+ " <th>open</th>\n",
648
+ " <th>high</th>\n",
649
+ " <th>low</th>\n",
650
+ " <th>close</th>\n",
651
+ " <th>volume</th>\n",
652
+ " <th>split_ratio</th>\n",
653
+ " <th>dividend</th>\n",
654
+ " <th>capital_gains</th>\n",
655
+ " </tr>\n",
656
+ " <tr>\n",
657
+ " <th>date</th>\n",
658
+ " <th></th>\n",
659
+ " <th></th>\n",
660
+ " <th></th>\n",
661
+ " <th></th>\n",
662
+ " <th></th>\n",
663
+ " <th></th>\n",
664
+ " <th></th>\n",
665
+ " <th></th>\n",
666
+ " </tr>\n",
667
+ " </thead>\n",
668
+ " <tbody>\n",
669
+ " <tr>\n",
670
+ " <th>2024-08-16 09:30:00</th>\n",
671
+ " <td>551.419983</td>\n",
672
+ " <td>551.929993</td>\n",
673
+ " <td>551.289978</td>\n",
674
+ " <td>551.349976</td>\n",
675
+ " <td>1881026</td>\n",
676
+ " <td>0</td>\n",
677
+ " <td>0</td>\n",
678
+ " <td>0</td>\n",
679
+ " </tr>\n",
680
+ " <tr>\n",
681
+ " <th>2024-08-16 09:31:00</th>\n",
682
+ " <td>551.349976</td>\n",
683
+ " <td>551.77002</td>\n",
684
+ " <td>551.26001</td>\n",
685
+ " <td>551.630005</td>\n",
686
+ " <td>230595</td>\n",
687
+ " <td>0</td>\n",
688
+ " <td>0</td>\n",
689
+ " <td>0</td>\n",
690
+ " </tr>\n",
691
+ " </tbody>\n",
692
+ "</table>\n",
693
+ "</div>"
694
+ ],
695
+ "text/plain": [
696
+ " open high low close volume \\\n",
697
+ "date \n",
698
+ "2024-08-16 09:30:00 551.419983 551.929993 551.289978 551.349976 1881026 \n",
699
+ "2024-08-16 09:31:00 551.349976 551.77002 551.26001 551.630005 230595 \n",
700
+ "\n",
701
+ " split_ratio dividend capital_gains \n",
702
+ "date \n",
703
+ "2024-08-16 09:30:00 0 0 0 \n",
704
+ "2024-08-16 09:31:00 0 0 0 "
705
+ ]
706
+ },
707
+ "metadata": {},
708
+ "output_type": "display_data"
709
+ }
710
+ ],
711
+ "source": [
712
+ "class HistoricalPrices:\n",
713
+ " def __init__(self, symbol, start_date, end_date, provider, **kwargs) -> None:\n",
714
+ " self.one: pd.DataFrame = (\n",
715
+ " obb.equity.price.historical(\n",
716
+ " symbol=symbol,\n",
717
+ " start_date=start_date,\n",
718
+ " end_date=end_date,\n",
719
+ " interval=\"1m\",\n",
720
+ " provider=provider,\n",
721
+ " **kwargs\n",
722
+ " )\n",
723
+ " .to_df()\n",
724
+ " .convert_dtypes()\n",
725
+ " )\n",
726
+ " self.five: pd.DataFrame = (\n",
727
+ " obb.equity.price.historical(\n",
728
+ " symbol=symbol,\n",
729
+ " start_date=start_date,\n",
730
+ " end_date=end_date,\n",
731
+ " interval=\"5m\",\n",
732
+ " provider=provider,\n",
733
+ " **kwargs\n",
734
+ " )\n",
735
+ " .to_df()\n",
736
+ " .convert_dtypes()\n",
737
+ " )\n",
738
+ " self.fifteen: pd.DataFrame = (\n",
739
+ " obb.equity.price.historical(\n",
740
+ " symbol=symbol,\n",
741
+ " start_date=start_date,\n",
742
+ " end_date=end_date,\n",
743
+ " interval=\"15m\",\n",
744
+ " provider=provider,\n",
745
+ " **kwargs\n",
746
+ " )\n",
747
+ " .to_df()\n",
748
+ " .convert_dtypes()\n",
749
+ " )\n",
750
+ " self.thirty: pd.DataFrame = (\n",
751
+ " obb.equity.price.historical(\n",
752
+ " symbol=symbol,\n",
753
+ " start_date=start_date,\n",
754
+ " end_date=end_date,\n",
755
+ " interval=\"30m\",\n",
756
+ " provider=provider,\n",
757
+ " **kwargs\n",
758
+ " )\n",
759
+ " .to_df()\n",
760
+ " .convert_dtypes()\n",
761
+ " )\n",
762
+ " self.sixty: pd.DataFrame = (\n",
763
+ " obb.equity.price.historical(\n",
764
+ " symbol=symbol,\n",
765
+ " start_date=start_date,\n",
766
+ " end_date=end_date,\n",
767
+ " interval=\"60m\",\n",
768
+ " provider=provider,\n",
769
+ " **kwargs\n",
770
+ " )\n",
771
+ " .to_df()\n",
772
+ " .convert_dtypes()\n",
773
+ " )\n",
774
+ " self.daily: pd.DataFrame = (\n",
775
+ " obb.equity.price.historical(\n",
776
+ " symbol=symbol,\n",
777
+ " start_date=start_date,\n",
778
+ " end_date=end_date,\n",
779
+ " interval=\"1d\",\n",
780
+ " provider=provider,\n",
781
+ " **kwargs\n",
782
+ " )\n",
783
+ " .to_df()\n",
784
+ " .convert_dtypes()\n",
785
+ " )\n",
786
+ " self.weekly: pd.DataFrame = (\n",
787
+ " obb.equity.price.historical(\n",
788
+ " symbol=symbol,\n",
789
+ " start_date=start_date,\n",
790
+ " end_date=end_date,\n",
791
+ " interval=\"1W\",\n",
792
+ " provider=provider,\n",
793
+ " **kwargs\n",
794
+ " )\n",
795
+ " .to_df()\n",
796
+ " .convert_dtypes()\n",
797
+ " )\n",
798
+ " self.monthly: pd.DataFrame = (\n",
799
+ " obb.equity.price.historical(\n",
800
+ " symbol=symbol,\n",
801
+ " start_date=start_date,\n",
802
+ " end_date=end_date,\n",
803
+ " interval=\"1M\",\n",
804
+ " provider=provider,\n",
805
+ " **kwargs\n",
806
+ " )\n",
807
+ " .to_df()\n",
808
+ " .convert_dtypes()\n",
809
+ " )\n",
810
+ "\n",
811
+ "\n",
812
+ "def load_historical(\n",
813
+ " symbol: str = \"\", start_date=None, end_date=None, provider=None, **kwargs\n",
814
+ ") -> HistoricalPrices:\n",
815
+ "\n",
816
+ " if symbol == \"\":\n",
817
+ " display(\"Please enter a ticker symbol\")\n",
818
+ " if provider is None:\n",
819
+ " provider = \"yfinance\"\n",
820
+ " prices = HistoricalPrices(symbol, start_date, end_date, provider, **kwargs)\n",
821
+ "\n",
822
+ " return prices\n",
823
+ "\n",
824
+ "\n",
825
+ "prices = load_historical(\"spy\")\n",
826
+ "display(prices.__dict__.keys())\n",
827
+ "display(prices.weekly.tail(2))\n",
828
+ "\n",
829
+ "display(prices.one.head(2))"
830
+ ]
831
+ },
832
+ {
833
+ "attachments": {},
834
+ "cell_type": "markdown",
835
+ "metadata": {},
836
+ "source": [
837
+ "To demonstrate the difference between sources, let's compare values for daily volume from several sources."
838
+ ]
839
+ },
840
+ {
841
+ "cell_type": "code",
842
+ "execution_count": 11,
843
+ "metadata": {},
844
+ "outputs": [
845
+ {
846
+ "data": {
847
+ "text/html": [
848
+ "<div>\n",
849
+ "<style scoped>\n",
850
+ " .dataframe tbody tr th:only-of-type {\n",
851
+ " vertical-align: middle;\n",
852
+ " }\n",
853
+ "\n",
854
+ " .dataframe tbody tr th {\n",
855
+ " vertical-align: top;\n",
856
+ " }\n",
857
+ "\n",
858
+ " .dataframe thead th {\n",
859
+ " text-align: right;\n",
860
+ " }\n",
861
+ "</style>\n",
862
+ "<table border=\"1\" class=\"dataframe\">\n",
863
+ " <thead>\n",
864
+ " <tr style=\"text-align: right;\">\n",
865
+ " <th></th>\n",
866
+ " <th>AV Volume</th>\n",
867
+ " <th>FMP Volume</th>\n",
868
+ " <th>Intrinio Volume</th>\n",
869
+ " <th>Yahoo Volume</th>\n",
870
+ " <th>Polygon Volume</th>\n",
871
+ " </tr>\n",
872
+ " <tr>\n",
873
+ " <th>date</th>\n",
874
+ " <th></th>\n",
875
+ " <th></th>\n",
876
+ " <th></th>\n",
877
+ " <th></th>\n",
878
+ " <th></th>\n",
879
+ " </tr>\n",
880
+ " </thead>\n",
881
+ " <tbody>\n",
882
+ " <tr>\n",
883
+ " <th>2024-08-09</th>\n",
884
+ " <td>45619558</td>\n",
885
+ " <td>45619558.0</td>\n",
886
+ " <td>45619558.0</td>\n",
887
+ " <td>45619600.0</td>\n",
888
+ " <td>45425963.0</td>\n",
889
+ " </tr>\n",
890
+ " <tr>\n",
891
+ " <th>2024-08-12</th>\n",
892
+ " <td>42542069</td>\n",
893
+ " <td>42542069.0</td>\n",
894
+ " <td>42542069.0</td>\n",
895
+ " <td>42542100.0</td>\n",
896
+ " <td>42533175.0</td>\n",
897
+ " </tr>\n",
898
+ " <tr>\n",
899
+ " <th>2024-08-13</th>\n",
900
+ " <td>52333073</td>\n",
901
+ " <td>52333073.0</td>\n",
902
+ " <td>52333073.0</td>\n",
903
+ " <td>52333100.0</td>\n",
904
+ " <td>50110167.0</td>\n",
905
+ " </tr>\n",
906
+ " <tr>\n",
907
+ " <th>2024-08-14</th>\n",
908
+ " <td>42446929</td>\n",
909
+ " <td>42446929.0</td>\n",
910
+ " <td>42446929.0</td>\n",
911
+ " <td>42446900.0</td>\n",
912
+ " <td>42362522.0</td>\n",
913
+ " </tr>\n",
914
+ " <tr>\n",
915
+ " <th>2024-08-15</th>\n",
916
+ " <td>60846812</td>\n",
917
+ " <td>60846812.0</td>\n",
918
+ " <td>60846812.0</td>\n",
919
+ " <td>60846800.0</td>\n",
920
+ " <td>60762738.0</td>\n",
921
+ " </tr>\n",
922
+ " <tr>\n",
923
+ " <th>2024-08-16</th>\n",
924
+ " <td>44430728</td>\n",
925
+ " <td>44430728.0</td>\n",
926
+ " <td>44430728.0</td>\n",
927
+ " <td>44430700.0</td>\n",
928
+ " <td>44368969.0</td>\n",
929
+ " </tr>\n",
930
+ " <tr>\n",
931
+ " <th>2024-08-19</th>\n",
932
+ " <td>39121793</td>\n",
933
+ " <td>39121793.0</td>\n",
934
+ " <td>39121793.0</td>\n",
935
+ " <td>39121800.0</td>\n",
936
+ " <td>38648958.0</td>\n",
937
+ " </tr>\n",
938
+ " <tr>\n",
939
+ " <th>2024-08-20</th>\n",
940
+ " <td>33732264</td>\n",
941
+ " <td>33732264.0</td>\n",
942
+ " <td>33732264.0</td>\n",
943
+ " <td>33732300.0</td>\n",
944
+ " <td>33693989.0</td>\n",
945
+ " </tr>\n",
946
+ " <tr>\n",
947
+ " <th>2024-08-21</th>\n",
948
+ " <td>41514600</td>\n",
949
+ " <td>38682509.0</td>\n",
950
+ " <td>41514600.0</td>\n",
951
+ " <td>41467000.0</td>\n",
952
+ " <td>41532360.0</td>\n",
953
+ " </tr>\n",
954
+ " </tbody>\n",
955
+ "</table>\n",
956
+ "</div>"
957
+ ],
958
+ "text/plain": [
959
+ " AV Volume FMP Volume Intrinio Volume Yahoo Volume \\\n",
960
+ "date \n",
961
+ "2024-08-09 45619558 45619558.0 45619558.0 45619600.0 \n",
962
+ "2024-08-12 42542069 42542069.0 42542069.0 42542100.0 \n",
963
+ "2024-08-13 52333073 52333073.0 52333073.0 52333100.0 \n",
964
+ "2024-08-14 42446929 42446929.0 42446929.0 42446900.0 \n",
965
+ "2024-08-15 60846812 60846812.0 60846812.0 60846800.0 \n",
966
+ "2024-08-16 44430728 44430728.0 44430728.0 44430700.0 \n",
967
+ "2024-08-19 39121793 39121793.0 39121793.0 39121800.0 \n",
968
+ "2024-08-20 33732264 33732264.0 33732264.0 33732300.0 \n",
969
+ "2024-08-21 41514600 38682509.0 41514600.0 41467000.0 \n",
970
+ "\n",
971
+ " Polygon Volume \n",
972
+ "date \n",
973
+ "2024-08-09 45425963.0 \n",
974
+ "2024-08-12 42533175.0 \n",
975
+ "2024-08-13 50110167.0 \n",
976
+ "2024-08-14 42362522.0 \n",
977
+ "2024-08-15 60762738.0 \n",
978
+ "2024-08-16 44368969.0 \n",
979
+ "2024-08-19 38648958.0 \n",
980
+ "2024-08-20 33693989.0 \n",
981
+ "2024-08-21 41532360.0 "
982
+ ]
983
+ },
984
+ "execution_count": 11,
985
+ "metadata": {},
986
+ "output_type": "execute_result"
987
+ }
988
+ ],
989
+ "source": [
990
+ "# Collect the data\n",
991
+ "\n",
992
+ "yahoo = obb.equity.price.historical(\"spy\", provider=\"yfinance\").to_df()\n",
993
+ "alphavantage = obb.equity.price.historical(\"spy\", provider=\"alpha_vantage\").to_df()\n",
994
+ "intrinio = obb.equity.price.historical(\"spy\", provider=\"intrinio\").to_df()\n",
995
+ "fmp = obb.equity.price.historical(\"spy\", provider=\"fmp\").to_df()\n",
996
+ "polygon = obb.equity.price.historical(\"spy\", provider=\"polygon\").to_df()\n",
997
+ "\n",
998
+ "# Make a new DataFrame with just the volume columns\n",
999
+ "compare = pd.DataFrame()\n",
1000
+ "compare[\"AV Volume\"] = alphavantage[\"volume\"].tail(10)\n",
1001
+ "compare[\"FMP Volume\"] = fmp[\"volume\"].tail(10)\n",
1002
+ "compare[\"Intrinio Volume\"] = intrinio[\"volume\"].tail(10)\n",
1003
+ "compare[\"Yahoo Volume\"] = yahoo[\"volume\"].tail(10)\n",
1004
+ "compare[\"Polygon Volume\"] = polygon[\"volume\"].tail(10)\n",
1005
+ "\n",
1006
+ "compare.dropna(how=\"any\")"
1007
+ ]
1008
+ },
1009
+ {
1010
+ "attachments": {},
1011
+ "cell_type": "markdown",
1012
+ "metadata": {},
1013
+ "source": [
1014
+ "## Other Types of Symbols\n",
1015
+ "\n",
1016
+ "Other types of assets and ticker symbols can be loaded from `obb.equity.price.historical()`, below are some examples but not an exhaustive list.\n",
1017
+ "\n",
1018
+ "### Share Classes\n",
1019
+ "\n",
1020
+ "Some sources use `-` as the distinction between a share class, e.g., `BRK-A` and `BRK-B`. Other formats include:\n",
1021
+ "\n",
1022
+ "- A period: `BRK.A`\n",
1023
+ "- A slash: `BRK/A`\n",
1024
+ "- No separator, the share class becomes the fourth or fifth letter.\n",
1025
+ "\n",
1026
+ "```python\n",
1027
+ "obb.equity.price.historical(\"brk.b\", provider=\"polygon\")\n",
1028
+ "```\n",
1029
+ "\n",
1030
+ "```python\n",
1031
+ "obb.equity.price.historical(\"brk-b\", provider=\"fmp\")\n",
1032
+ "```\n",
1033
+ "\n",
1034
+ "While some providers handle the different formats on their end, others do not. This is something to consider when no results are returned from one source. Some may even use a combination, or accept multiple variations. Sometimes there is no real logic behind the additional characters, `GOOGL` vs. `GOOG`. These are known unknown variables of ticker symbology, what's good for one source may return errors from another. \n",
1035
+ "\n",
1036
+ "### Regional Identifiers\n",
1037
+ "\n",
1038
+ "With providers supporting market data from multiple jurisdictions, the most common method for requesting data outside of US-listings is to append a suffix to the ticker symbol (e.g., `RELIANCE.NS` for Indian equities). Formats may be unique to a provider, so it is best to review the source's documentation for an overview of their specific conventions. [This page](https://help.yahoo.com/kb/SLN2310.html) on Yahoo describes how they format symbols, which many others follow to some degree.\n",
1039
+ "\n",
1040
+ "### Indexes\n",
1041
+ "\n",
1042
+ "Sources will have their own treatment of these symbols, some examples are:\n",
1043
+ "\n",
1044
+ "- YahooFinance/FMP/CBOE: ^RUT\n",
1045
+ "- Polygon: I:NDX\n",
1046
+ "\n",
1047
+ "### Currencies\n",
1048
+ "\n",
1049
+ "FX symbols face the same dilemna as share classes, there are several variations of the same symbol.\n",
1050
+ "\n",
1051
+ "- YahooFinance: `EURUSD=X`\n",
1052
+ "- Polygon: `C:EURUSD`\n",
1053
+ "- AlphaVantage/FMP: `EURUSD`\n",
1054
+ "\n",
1055
+ "**The symbol prefixes are handled internally when `obb.currency.price.historical()` is used to enter a pair with no extra characters.**\n",
1056
+ "\n",
1057
+ "### Crypto\n",
1058
+ "\n",
1059
+ "Similar, but different to FX tickers.\n",
1060
+ "\n",
1061
+ "- YahooFinance: `BTC-USD`\n",
1062
+ "- Polygon: `X:BTCUSD`\n",
1063
+ "- AlphaVantage/FMP: `BTCUSD`\n",
1064
+ "\n",
1065
+ "**The symbol prefixes are handled internally when `obb.crypto.price.historical()` is used to enter a pair with no extra characters and placing the fiat currency second.**\n",
1066
+ "\n",
1067
+ "### Futures\n",
1068
+ "\n",
1069
+ "Historical prices for active contracts, and the continuation chart, can be fetched via `yfinance`.\n",
1070
+ "\n",
1071
+ "- Continuous front-month: `CL=F`\n",
1072
+ "- December 2023 contract: `CLZ24.NYM`\n",
1073
+ "- March 2024 contract: `CLH24.NYM`\n",
1074
+ "\n",
1075
+ "Individual contracts will require knowing which of the CME venues the future is listed on. `[\"NYM\", \"NYB\", \"CME\", \"CBT\"]`.\n",
1076
+ "\n",
1077
+ "### Options\n",
1078
+ "\n",
1079
+ "Individual options contracts are also loadable from `openbb.equity.price.historical()`.\n",
1080
+ "\n",
1081
+ "- YahooFinance: `SPY241220P00400000`\n",
1082
+ "- Polygon: `O:SPY241220P00400000`"
1083
+ ]
1084
+ },
1085
+ {
1086
+ "attachments": {},
1087
+ "cell_type": "markdown",
1088
+ "metadata": {},
1089
+ "source": [
1090
+ "These examples represent only a few methods for fetching historical price data. Explore the contents of each module to find more!"
1091
+ ]
1092
+ },
1093
+ {
1094
+ "cell_type": "code",
1095
+ "execution_count": 13,
1096
+ "metadata": {},
1097
+ "outputs": [
1098
+ {
1099
+ "data": {
1100
+ "text/html": [
1101
+ "<div>\n",
1102
+ "<style scoped>\n",
1103
+ " .dataframe tbody tr th:only-of-type {\n",
1104
+ " vertical-align: middle;\n",
1105
+ " }\n",
1106
+ "\n",
1107
+ " .dataframe tbody tr th {\n",
1108
+ " vertical-align: top;\n",
1109
+ " }\n",
1110
+ "\n",
1111
+ " .dataframe thead th {\n",
1112
+ " text-align: right;\n",
1113
+ " }\n",
1114
+ "</style>\n",
1115
+ "<table border=\"1\" class=\"dataframe\">\n",
1116
+ " <thead>\n",
1117
+ " <tr style=\"text-align: right;\">\n",
1118
+ " <th></th>\n",
1119
+ " <th>open</th>\n",
1120
+ " <th>high</th>\n",
1121
+ " <th>low</th>\n",
1122
+ " <th>close</th>\n",
1123
+ " <th>volume</th>\n",
1124
+ " <th>split_ratio</th>\n",
1125
+ " <th>dividend</th>\n",
1126
+ " </tr>\n",
1127
+ " <tr>\n",
1128
+ " <th>date</th>\n",
1129
+ " <th></th>\n",
1130
+ " <th></th>\n",
1131
+ " <th></th>\n",
1132
+ " <th></th>\n",
1133
+ " <th></th>\n",
1134
+ " <th></th>\n",
1135
+ " <th></th>\n",
1136
+ " </tr>\n",
1137
+ " </thead>\n",
1138
+ " <tbody>\n",
1139
+ " <tr>\n",
1140
+ " <th>2023-08-22</th>\n",
1141
+ " <td>25.10</td>\n",
1142
+ " <td>25.100000</td>\n",
1143
+ " <td>25.10</td>\n",
1144
+ " <td>25.100000</td>\n",
1145
+ " <td>11</td>\n",
1146
+ " <td>0.0</td>\n",
1147
+ " <td>0.0</td>\n",
1148
+ " </tr>\n",
1149
+ " <tr>\n",
1150
+ " <th>2023-08-23</th>\n",
1151
+ " <td>25.00</td>\n",
1152
+ " <td>25.000000</td>\n",
1153
+ " <td>24.50</td>\n",
1154
+ " <td>24.500000</td>\n",
1155
+ " <td>2</td>\n",
1156
+ " <td>0.0</td>\n",
1157
+ " <td>0.0</td>\n",
1158
+ " </tr>\n",
1159
+ " <tr>\n",
1160
+ " <th>2023-08-24</th>\n",
1161
+ " <td>25.00</td>\n",
1162
+ " <td>25.200001</td>\n",
1163
+ " <td>25.00</td>\n",
1164
+ " <td>25.200001</td>\n",
1165
+ " <td>2</td>\n",
1166
+ " <td>0.0</td>\n",
1167
+ " <td>0.0</td>\n",
1168
+ " </tr>\n",
1169
+ " <tr>\n",
1170
+ " <th>2023-08-25</th>\n",
1171
+ " <td>25.35</td>\n",
1172
+ " <td>25.350000</td>\n",
1173
+ " <td>24.18</td>\n",
1174
+ " <td>24.549999</td>\n",
1175
+ " <td>0</td>\n",
1176
+ " <td>0.0</td>\n",
1177
+ " <td>0.0</td>\n",
1178
+ " </tr>\n",
1179
+ " <tr>\n",
1180
+ " <th>2023-08-29</th>\n",
1181
+ " <td>24.00</td>\n",
1182
+ " <td>24.700001</td>\n",
1183
+ " <td>22.50</td>\n",
1184
+ " <td>23.910000</td>\n",
1185
+ " <td>0</td>\n",
1186
+ " <td>0.0</td>\n",
1187
+ " <td>0.0</td>\n",
1188
+ " </tr>\n",
1189
+ " <tr>\n",
1190
+ " <th>...</th>\n",
1191
+ " <td>...</td>\n",
1192
+ " <td>...</td>\n",
1193
+ " <td>...</td>\n",
1194
+ " <td>...</td>\n",
1195
+ " <td>...</td>\n",
1196
+ " <td>...</td>\n",
1197
+ " <td>...</td>\n",
1198
+ " </tr>\n",
1199
+ " <tr>\n",
1200
+ " <th>2024-08-16</th>\n",
1201
+ " <td>5.95</td>\n",
1202
+ " <td>6.100000</td>\n",
1203
+ " <td>5.95</td>\n",
1204
+ " <td>5.990000</td>\n",
1205
+ " <td>4</td>\n",
1206
+ " <td>0.0</td>\n",
1207
+ " <td>0.0</td>\n",
1208
+ " </tr>\n",
1209
+ " <tr>\n",
1210
+ " <th>2024-08-19</th>\n",
1211
+ " <td>5.92</td>\n",
1212
+ " <td>5.920000</td>\n",
1213
+ " <td>5.71</td>\n",
1214
+ " <td>5.710000</td>\n",
1215
+ " <td>40</td>\n",
1216
+ " <td>0.0</td>\n",
1217
+ " <td>0.0</td>\n",
1218
+ " </tr>\n",
1219
+ " <tr>\n",
1220
+ " <th>2024-08-20</th>\n",
1221
+ " <td>5.73</td>\n",
1222
+ " <td>6.240000</td>\n",
1223
+ " <td>5.73</td>\n",
1224
+ " <td>6.240000</td>\n",
1225
+ " <td>42</td>\n",
1226
+ " <td>0.0</td>\n",
1227
+ " <td>0.0</td>\n",
1228
+ " </tr>\n",
1229
+ " <tr>\n",
1230
+ " <th>2024-08-21</th>\n",
1231
+ " <td>6.28</td>\n",
1232
+ " <td>6.660000</td>\n",
1233
+ " <td>6.28</td>\n",
1234
+ " <td>6.440000</td>\n",
1235
+ " <td>276</td>\n",
1236
+ " <td>0.0</td>\n",
1237
+ " <td>0.0</td>\n",
1238
+ " </tr>\n",
1239
+ " <tr>\n",
1240
+ " <th>2024-08-22</th>\n",
1241
+ " <td>6.29</td>\n",
1242
+ " <td>6.660000</td>\n",
1243
+ " <td>6.29</td>\n",
1244
+ " <td>6.660000</td>\n",
1245
+ " <td>4</td>\n",
1246
+ " <td>0.0</td>\n",
1247
+ " <td>0.0</td>\n",
1248
+ " </tr>\n",
1249
+ " </tbody>\n",
1250
+ "</table>\n",
1251
+ "<p>234 rows × 7 columns</p>\n",
1252
+ "</div>"
1253
+ ],
1254
+ "text/plain": [
1255
+ " open high low close volume split_ratio dividend\n",
1256
+ "date \n",
1257
+ "2023-08-22 25.10 25.100000 25.10 25.100000 11 0.0 0.0\n",
1258
+ "2023-08-23 25.00 25.000000 24.50 24.500000 2 0.0 0.0\n",
1259
+ "2023-08-24 25.00 25.200001 25.00 25.200001 2 0.0 0.0\n",
1260
+ "2023-08-25 25.35 25.350000 24.18 24.549999 0 0.0 0.0\n",
1261
+ "2023-08-29 24.00 24.700001 22.50 23.910000 0 0.0 0.0\n",
1262
+ "... ... ... ... ... ... ... ...\n",
1263
+ "2024-08-16 5.95 6.100000 5.95 5.990000 4 0.0 0.0\n",
1264
+ "2024-08-19 5.92 5.920000 5.71 5.710000 40 0.0 0.0\n",
1265
+ "2024-08-20 5.73 6.240000 5.73 6.240000 42 0.0 0.0\n",
1266
+ "2024-08-21 6.28 6.660000 6.28 6.440000 276 0.0 0.0\n",
1267
+ "2024-08-22 6.29 6.660000 6.29 6.660000 4 0.0 0.0\n",
1268
+ "\n",
1269
+ "[234 rows x 7 columns]"
1270
+ ]
1271
+ },
1272
+ "execution_count": 13,
1273
+ "metadata": {},
1274
+ "output_type": "execute_result"
1275
+ }
1276
+ ],
1277
+ "source": [
1278
+ "obb.equity.price.historical(\"SPY251219P00400000\", provider=\"yfinance\").to_df()"
1279
+ ]
1280
+ },
1281
+ {
1282
+ "cell_type": "code",
1283
+ "execution_count": 16,
1284
+ "metadata": {},
1285
+ "outputs": [
1286
+ {
1287
+ "data": {
1288
+ "text/html": [
1289
+ "<div>\n",
1290
+ "<style scoped>\n",
1291
+ " .dataframe tbody tr th:only-of-type {\n",
1292
+ " vertical-align: middle;\n",
1293
+ " }\n",
1294
+ "\n",
1295
+ " .dataframe tbody tr th {\n",
1296
+ " vertical-align: top;\n",
1297
+ " }\n",
1298
+ "\n",
1299
+ " .dataframe thead th {\n",
1300
+ " text-align: right;\n",
1301
+ " }\n",
1302
+ "</style>\n",
1303
+ "<table border=\"1\" class=\"dataframe\">\n",
1304
+ " <thead>\n",
1305
+ " <tr style=\"text-align: right;\">\n",
1306
+ " <th></th>\n",
1307
+ " <th>open</th>\n",
1308
+ " <th>high</th>\n",
1309
+ " <th>low</th>\n",
1310
+ " <th>close</th>\n",
1311
+ " <th>volume</th>\n",
1312
+ " </tr>\n",
1313
+ " <tr>\n",
1314
+ " <th>date</th>\n",
1315
+ " <th></th>\n",
1316
+ " <th></th>\n",
1317
+ " <th></th>\n",
1318
+ " <th></th>\n",
1319
+ " <th></th>\n",
1320
+ " </tr>\n",
1321
+ " </thead>\n",
1322
+ " <tbody>\n",
1323
+ " <tr>\n",
1324
+ " <th>1978-01-03</th>\n",
1325
+ " <td>94.74</td>\n",
1326
+ " <td>95.15</td>\n",
1327
+ " <td>93.49</td>\n",
1328
+ " <td>93.82</td>\n",
1329
+ " <td>0</td>\n",
1330
+ " </tr>\n",
1331
+ " <tr>\n",
1332
+ " <th>1978-01-04</th>\n",
1333
+ " <td>93.16</td>\n",
1334
+ " <td>94.10</td>\n",
1335
+ " <td>92.57</td>\n",
1336
+ " <td>93.52</td>\n",
1337
+ " <td>0</td>\n",
1338
+ " </tr>\n",
1339
+ " <tr>\n",
1340
+ " <th>1978-01-05</th>\n",
1341
+ " <td>94.18</td>\n",
1342
+ " <td>94.53</td>\n",
1343
+ " <td>92.51</td>\n",
1344
+ " <td>92.74</td>\n",
1345
+ " <td>0</td>\n",
1346
+ " </tr>\n",
1347
+ " <tr>\n",
1348
+ " <th>1978-01-06</th>\n",
1349
+ " <td>92.06</td>\n",
1350
+ " <td>92.66</td>\n",
1351
+ " <td>91.05</td>\n",
1352
+ " <td>91.62</td>\n",
1353
+ " <td>0</td>\n",
1354
+ " </tr>\n",
1355
+ " <tr>\n",
1356
+ " <th>1978-01-09</th>\n",
1357
+ " <td>90.82</td>\n",
1358
+ " <td>91.48</td>\n",
1359
+ " <td>89.97</td>\n",
1360
+ " <td>90.64</td>\n",
1361
+ " <td>0</td>\n",
1362
+ " </tr>\n",
1363
+ " <tr>\n",
1364
+ " <th>...</th>\n",
1365
+ " <td>...</td>\n",
1366
+ " <td>...</td>\n",
1367
+ " <td>...</td>\n",
1368
+ " <td>...</td>\n",
1369
+ " <td>...</td>\n",
1370
+ " </tr>\n",
1371
+ " <tr>\n",
1372
+ " <th>2024-08-15</th>\n",
1373
+ " <td>5501.13</td>\n",
1374
+ " <td>5546.23</td>\n",
1375
+ " <td>5501.13</td>\n",
1376
+ " <td>5543.22</td>\n",
1377
+ " <td>0</td>\n",
1378
+ " </tr>\n",
1379
+ " <tr>\n",
1380
+ " <th>2024-08-16</th>\n",
1381
+ " <td>5530.50</td>\n",
1382
+ " <td>5561.98</td>\n",
1383
+ " <td>5525.17</td>\n",
1384
+ " <td>5554.25</td>\n",
1385
+ " <td>0</td>\n",
1386
+ " </tr>\n",
1387
+ " <tr>\n",
1388
+ " <th>2024-08-19</th>\n",
1389
+ " <td>5557.23</td>\n",
1390
+ " <td>5608.30</td>\n",
1391
+ " <td>5550.74</td>\n",
1392
+ " <td>5608.25</td>\n",
1393
+ " <td>0</td>\n",
1394
+ " </tr>\n",
1395
+ " <tr>\n",
1396
+ " <th>2024-08-20</th>\n",
1397
+ " <td>5602.88</td>\n",
1398
+ " <td>5620.51</td>\n",
1399
+ " <td>5585.50</td>\n",
1400
+ " <td>5597.12</td>\n",
1401
+ " <td>0</td>\n",
1402
+ " </tr>\n",
1403
+ " <tr>\n",
1404
+ " <th>2024-08-21</th>\n",
1405
+ " <td>5603.09</td>\n",
1406
+ " <td>5632.68</td>\n",
1407
+ " <td>5591.57</td>\n",
1408
+ " <td>5620.85</td>\n",
1409
+ " <td>0</td>\n",
1410
+ " </tr>\n",
1411
+ " </tbody>\n",
1412
+ "</table>\n",
1413
+ "<p>11509 rows × 5 columns</p>\n",
1414
+ "</div>"
1415
+ ],
1416
+ "text/plain": [
1417
+ " open high low close volume\n",
1418
+ "date \n",
1419
+ "1978-01-03 94.74 95.15 93.49 93.82 0\n",
1420
+ "1978-01-04 93.16 94.10 92.57 93.52 0\n",
1421
+ "1978-01-05 94.18 94.53 92.51 92.74 0\n",
1422
+ "1978-01-06 92.06 92.66 91.05 91.62 0\n",
1423
+ "1978-01-09 90.82 91.48 89.97 90.64 0\n",
1424
+ "... ... ... ... ... ...\n",
1425
+ "2024-08-15 5501.13 5546.23 5501.13 5543.22 0\n",
1426
+ "2024-08-16 5530.50 5561.98 5525.17 5554.25 0\n",
1427
+ "2024-08-19 5557.23 5608.30 5550.74 5608.25 0\n",
1428
+ "2024-08-20 5602.88 5620.51 5585.50 5597.12 0\n",
1429
+ "2024-08-21 5603.09 5632.68 5591.57 5620.85 0\n",
1430
+ "\n",
1431
+ "[11509 rows x 5 columns]"
1432
+ ]
1433
+ },
1434
+ "execution_count": 16,
1435
+ "metadata": {},
1436
+ "output_type": "execute_result"
1437
+ }
1438
+ ],
1439
+ "source": [
1440
+ "obb.equity.price.historical(\"SPX\", provider=\"cboe\").to_df()"
1441
+ ]
1442
+ },
1443
+ {
1444
+ "cell_type": "code",
1445
+ "execution_count": 17,
1446
+ "metadata": {},
1447
+ "outputs": [
1448
+ {
1449
+ "data": {
1450
+ "text/html": [
1451
+ "<div>\n",
1452
+ "<style scoped>\n",
1453
+ " .dataframe tbody tr th:only-of-type {\n",
1454
+ " vertical-align: middle;\n",
1455
+ " }\n",
1456
+ "\n",
1457
+ " .dataframe tbody tr th {\n",
1458
+ " vertical-align: top;\n",
1459
+ " }\n",
1460
+ "\n",
1461
+ " .dataframe thead th {\n",
1462
+ " text-align: right;\n",
1463
+ " }\n",
1464
+ "</style>\n",
1465
+ "<table border=\"1\" class=\"dataframe\">\n",
1466
+ " <thead>\n",
1467
+ " <tr style=\"text-align: right;\">\n",
1468
+ " <th></th>\n",
1469
+ " <th>open</th>\n",
1470
+ " <th>high</th>\n",
1471
+ " <th>low</th>\n",
1472
+ " <th>close</th>\n",
1473
+ " <th>volume</th>\n",
1474
+ " <th>vwap</th>\n",
1475
+ " <th>adj_close</th>\n",
1476
+ " <th>unadjusted_volume</th>\n",
1477
+ " <th>change</th>\n",
1478
+ " <th>change_percent</th>\n",
1479
+ " </tr>\n",
1480
+ " <tr>\n",
1481
+ " <th>date</th>\n",
1482
+ " <th></th>\n",
1483
+ " <th></th>\n",
1484
+ " <th></th>\n",
1485
+ " <th></th>\n",
1486
+ " <th></th>\n",
1487
+ " <th></th>\n",
1488
+ " <th></th>\n",
1489
+ " <th></th>\n",
1490
+ " <th></th>\n",
1491
+ " <th></th>\n",
1492
+ " </tr>\n",
1493
+ " </thead>\n",
1494
+ " <tbody>\n",
1495
+ " <tr>\n",
1496
+ " <th>2023-08-22</th>\n",
1497
+ " <td>4415.33008</td>\n",
1498
+ " <td>4418.58984</td>\n",
1499
+ " <td>4382.77002</td>\n",
1500
+ " <td>4387.54980</td>\n",
1501
+ " <td>3522760000</td>\n",
1502
+ " <td>4396.30</td>\n",
1503
+ " <td>4387.54980</td>\n",
1504
+ " <td>3.522760e+09</td>\n",
1505
+ " <td>-27.78028</td>\n",
1506
+ " <td>-0.006292</td>\n",
1507
+ " </tr>\n",
1508
+ " <tr>\n",
1509
+ " <th>2023-08-23</th>\n",
1510
+ " <td>4396.43994</td>\n",
1511
+ " <td>4443.18018</td>\n",
1512
+ " <td>4396.43994</td>\n",
1513
+ " <td>4436.00977</td>\n",
1514
+ " <td>3837270000</td>\n",
1515
+ " <td>4425.21</td>\n",
1516
+ " <td>4436.00977</td>\n",
1517
+ " <td>3.837270e+09</td>\n",
1518
+ " <td>39.56983</td>\n",
1519
+ " <td>0.009000</td>\n",
1520
+ " </tr>\n",
1521
+ " <tr>\n",
1522
+ " <th>2023-08-24</th>\n",
1523
+ " <td>4455.16016</td>\n",
1524
+ " <td>4458.29980</td>\n",
1525
+ " <td>4375.54980</td>\n",
1526
+ " <td>4376.31006</td>\n",
1527
+ " <td>3723470000</td>\n",
1528
+ " <td>4403.39</td>\n",
1529
+ " <td>4376.31006</td>\n",
1530
+ " <td>3.723470e+09</td>\n",
1531
+ " <td>-78.85010</td>\n",
1532
+ " <td>-0.017700</td>\n",
1533
+ " </tr>\n",
1534
+ " <tr>\n",
1535
+ " <th>2023-08-25</th>\n",
1536
+ " <td>4389.37988</td>\n",
1537
+ " <td>4418.45996</td>\n",
1538
+ " <td>4356.29004</td>\n",
1539
+ " <td>4405.70996</td>\n",
1540
+ " <td>3296180000</td>\n",
1541
+ " <td>4393.49</td>\n",
1542
+ " <td>4405.70996</td>\n",
1543
+ " <td>3.296180e+09</td>\n",
1544
+ " <td>16.33008</td>\n",
1545
+ " <td>0.003720</td>\n",
1546
+ " </tr>\n",
1547
+ " <tr>\n",
1548
+ " <th>2023-08-28</th>\n",
1549
+ " <td>4426.02979</td>\n",
1550
+ " <td>4439.56006</td>\n",
1551
+ " <td>4414.97998</td>\n",
1552
+ " <td>4433.31006</td>\n",
1553
+ " <td>2957230000</td>\n",
1554
+ " <td>4429.28</td>\n",
1555
+ " <td>4433.31006</td>\n",
1556
+ " <td>2.957230e+09</td>\n",
1557
+ " <td>7.28027</td>\n",
1558
+ " <td>0.001645</td>\n",
1559
+ " </tr>\n",
1560
+ " <tr>\n",
1561
+ " <th>...</th>\n",
1562
+ " <td>...</td>\n",
1563
+ " <td>...</td>\n",
1564
+ " <td>...</td>\n",
1565
+ " <td>...</td>\n",
1566
+ " <td>...</td>\n",
1567
+ " <td>...</td>\n",
1568
+ " <td>...</td>\n",
1569
+ " <td>...</td>\n",
1570
+ " <td>...</td>\n",
1571
+ " <td>...</td>\n",
1572
+ " </tr>\n",
1573
+ " <tr>\n",
1574
+ " <th>2024-08-16</th>\n",
1575
+ " <td>5530.50000</td>\n",
1576
+ " <td>5561.97998</td>\n",
1577
+ " <td>5525.16992</td>\n",
1578
+ " <td>5554.25000</td>\n",
1579
+ " <td>3357690000</td>\n",
1580
+ " <td>5542.97</td>\n",
1581
+ " <td>5554.25000</td>\n",
1582
+ " <td>3.357690e+09</td>\n",
1583
+ " <td>23.75000</td>\n",
1584
+ " <td>0.004294</td>\n",
1585
+ " </tr>\n",
1586
+ " <tr>\n",
1587
+ " <th>2024-08-19</th>\n",
1588
+ " <td>5557.22998</td>\n",
1589
+ " <td>5608.29981</td>\n",
1590
+ " <td>5550.74023</td>\n",
1591
+ " <td>5608.25000</td>\n",
1592
+ " <td>3222050000</td>\n",
1593
+ " <td>5581.13</td>\n",
1594
+ " <td>5608.25000</td>\n",
1595
+ " <td>3.222050e+09</td>\n",
1596
+ " <td>51.02002</td>\n",
1597
+ " <td>0.009181</td>\n",
1598
+ " </tr>\n",
1599
+ " <tr>\n",
1600
+ " <th>2024-08-20</th>\n",
1601
+ " <td>5602.87988</td>\n",
1602
+ " <td>5620.50977</td>\n",
1603
+ " <td>5585.50000</td>\n",
1604
+ " <td>5597.12012</td>\n",
1605
+ " <td>2994420000</td>\n",
1606
+ " <td>5601.50</td>\n",
1607
+ " <td>5597.12012</td>\n",
1608
+ " <td>2.994420e+09</td>\n",
1609
+ " <td>-5.75976</td>\n",
1610
+ " <td>-0.001028</td>\n",
1611
+ " </tr>\n",
1612
+ " <tr>\n",
1613
+ " <th>2024-08-21</th>\n",
1614
+ " <td>5603.08984</td>\n",
1615
+ " <td>5632.68018</td>\n",
1616
+ " <td>5591.56982</td>\n",
1617
+ " <td>5620.85010</td>\n",
1618
+ " <td>1982137065</td>\n",
1619
+ " <td>5612.05</td>\n",
1620
+ " <td>5620.85010</td>\n",
1621
+ " <td>1.982137e+09</td>\n",
1622
+ " <td>17.76026</td>\n",
1623
+ " <td>0.003170</td>\n",
1624
+ " </tr>\n",
1625
+ " <tr>\n",
1626
+ " <th>2024-08-22</th>\n",
1627
+ " <td>5637.77000</td>\n",
1628
+ " <td>5643.22000</td>\n",
1629
+ " <td>5563.54000</td>\n",
1630
+ " <td>5577.27000</td>\n",
1631
+ " <td>1218069912</td>\n",
1632
+ " <td>5594.68</td>\n",
1633
+ " <td>5577.27000</td>\n",
1634
+ " <td>1.218070e+09</td>\n",
1635
+ " <td>-60.50000</td>\n",
1636
+ " <td>-0.010731</td>\n",
1637
+ " </tr>\n",
1638
+ " </tbody>\n",
1639
+ "</table>\n",
1640
+ "<p>253 rows × 10 columns</p>\n",
1641
+ "</div>"
1642
+ ],
1643
+ "text/plain": [
1644
+ " open high low close volume \\\n",
1645
+ "date \n",
1646
+ "2023-08-22 4415.33008 4418.58984 4382.77002 4387.54980 3522760000 \n",
1647
+ "2023-08-23 4396.43994 4443.18018 4396.43994 4436.00977 3837270000 \n",
1648
+ "2023-08-24 4455.16016 4458.29980 4375.54980 4376.31006 3723470000 \n",
1649
+ "2023-08-25 4389.37988 4418.45996 4356.29004 4405.70996 3296180000 \n",
1650
+ "2023-08-28 4426.02979 4439.56006 4414.97998 4433.31006 2957230000 \n",
1651
+ "... ... ... ... ... ... \n",
1652
+ "2024-08-16 5530.50000 5561.97998 5525.16992 5554.25000 3357690000 \n",
1653
+ "2024-08-19 5557.22998 5608.29981 5550.74023 5608.25000 3222050000 \n",
1654
+ "2024-08-20 5602.87988 5620.50977 5585.50000 5597.12012 2994420000 \n",
1655
+ "2024-08-21 5603.08984 5632.68018 5591.56982 5620.85010 1982137065 \n",
1656
+ "2024-08-22 5637.77000 5643.22000 5563.54000 5577.27000 1218069912 \n",
1657
+ "\n",
1658
+ " vwap adj_close unadjusted_volume change change_percent \n",
1659
+ "date \n",
1660
+ "2023-08-22 4396.30 4387.54980 3.522760e+09 -27.78028 -0.006292 \n",
1661
+ "2023-08-23 4425.21 4436.00977 3.837270e+09 39.56983 0.009000 \n",
1662
+ "2023-08-24 4403.39 4376.31006 3.723470e+09 -78.85010 -0.017700 \n",
1663
+ "2023-08-25 4393.49 4405.70996 3.296180e+09 16.33008 0.003720 \n",
1664
+ "2023-08-28 4429.28 4433.31006 2.957230e+09 7.28027 0.001645 \n",
1665
+ "... ... ... ... ... ... \n",
1666
+ "2024-08-16 5542.97 5554.25000 3.357690e+09 23.75000 0.004294 \n",
1667
+ "2024-08-19 5581.13 5608.25000 3.222050e+09 51.02002 0.009181 \n",
1668
+ "2024-08-20 5601.50 5597.12012 2.994420e+09 -5.75976 -0.001028 \n",
1669
+ "2024-08-21 5612.05 5620.85010 1.982137e+09 17.76026 0.003170 \n",
1670
+ "2024-08-22 5594.68 5577.27000 1.218070e+09 -60.50000 -0.010731 \n",
1671
+ "\n",
1672
+ "[253 rows x 10 columns]"
1673
+ ]
1674
+ },
1675
+ "execution_count": 17,
1676
+ "metadata": {},
1677
+ "output_type": "execute_result"
1678
+ }
1679
+ ],
1680
+ "source": [
1681
+ "obb.equity.price.historical(\"^SPX\", provider=\"fmp\").to_df()"
1682
+ ]
1683
+ },
1684
+ {
1685
+ "cell_type": "code",
1686
+ "execution_count": 19,
1687
+ "metadata": {},
1688
+ "outputs": [
1689
+ {
1690
+ "data": {
1691
+ "text/html": [
1692
+ "<div>\n",
1693
+ "<style scoped>\n",
1694
+ " .dataframe tbody tr th:only-of-type {\n",
1695
+ " vertical-align: middle;\n",
1696
+ " }\n",
1697
+ "\n",
1698
+ " .dataframe tbody tr th {\n",
1699
+ " vertical-align: top;\n",
1700
+ " }\n",
1701
+ "\n",
1702
+ " .dataframe thead th {\n",
1703
+ " text-align: right;\n",
1704
+ " }\n",
1705
+ "</style>\n",
1706
+ "<table border=\"1\" class=\"dataframe\">\n",
1707
+ " <thead>\n",
1708
+ " <tr style=\"text-align: right;\">\n",
1709
+ " <th></th>\n",
1710
+ " <th>open</th>\n",
1711
+ " <th>high</th>\n",
1712
+ " <th>low</th>\n",
1713
+ " <th>close</th>\n",
1714
+ " <th>volume</th>\n",
1715
+ " <th>split_ratio</th>\n",
1716
+ " <th>dividend</th>\n",
1717
+ " </tr>\n",
1718
+ " <tr>\n",
1719
+ " <th>date</th>\n",
1720
+ " <th></th>\n",
1721
+ " <th></th>\n",
1722
+ " <th></th>\n",
1723
+ " <th></th>\n",
1724
+ " <th></th>\n",
1725
+ " <th></th>\n",
1726
+ " <th></th>\n",
1727
+ " </tr>\n",
1728
+ " </thead>\n",
1729
+ " <tbody>\n",
1730
+ " <tr>\n",
1731
+ " <th>2023-08-22</th>\n",
1732
+ " <td>71.419998</td>\n",
1733
+ " <td>71.949997</td>\n",
1734
+ " <td>71.029999</td>\n",
1735
+ " <td>71.160004</td>\n",
1736
+ " <td>5342</td>\n",
1737
+ " <td>0.0</td>\n",
1738
+ " <td>0.0</td>\n",
1739
+ " </tr>\n",
1740
+ " <tr>\n",
1741
+ " <th>2023-08-23</th>\n",
1742
+ " <td>71.120003</td>\n",
1743
+ " <td>71.320000</td>\n",
1744
+ " <td>69.709999</td>\n",
1745
+ " <td>70.730003</td>\n",
1746
+ " <td>5139</td>\n",
1747
+ " <td>0.0</td>\n",
1748
+ " <td>0.0</td>\n",
1749
+ " </tr>\n",
1750
+ " <tr>\n",
1751
+ " <th>2023-08-24</th>\n",
1752
+ " <td>70.459999</td>\n",
1753
+ " <td>70.860001</td>\n",
1754
+ " <td>69.870003</td>\n",
1755
+ " <td>70.190002</td>\n",
1756
+ " <td>7594</td>\n",
1757
+ " <td>0.0</td>\n",
1758
+ " <td>0.0</td>\n",
1759
+ " </tr>\n",
1760
+ " <tr>\n",
1761
+ " <th>2023-08-25</th>\n",
1762
+ " <td>70.050003</td>\n",
1763
+ " <td>70.889999</td>\n",
1764
+ " <td>69.470001</td>\n",
1765
+ " <td>70.680000</td>\n",
1766
+ " <td>9328</td>\n",
1767
+ " <td>0.0</td>\n",
1768
+ " <td>0.0</td>\n",
1769
+ " </tr>\n",
1770
+ " <tr>\n",
1771
+ " <th>2023-08-28</th>\n",
1772
+ " <td>70.690002</td>\n",
1773
+ " <td>71.190002</td>\n",
1774
+ " <td>70.239998</td>\n",
1775
+ " <td>70.480003</td>\n",
1776
+ " <td>6234</td>\n",
1777
+ " <td>0.0</td>\n",
1778
+ " <td>0.0</td>\n",
1779
+ " </tr>\n",
1780
+ " <tr>\n",
1781
+ " <th>...</th>\n",
1782
+ " <td>...</td>\n",
1783
+ " <td>...</td>\n",
1784
+ " <td>...</td>\n",
1785
+ " <td>...</td>\n",
1786
+ " <td>...</td>\n",
1787
+ " <td>...</td>\n",
1788
+ " <td>...</td>\n",
1789
+ " </tr>\n",
1790
+ " <tr>\n",
1791
+ " <th>2024-08-16</th>\n",
1792
+ " <td>70.940002</td>\n",
1793
+ " <td>70.989998</td>\n",
1794
+ " <td>69.440002</td>\n",
1795
+ " <td>70.019997</td>\n",
1796
+ " <td>38165</td>\n",
1797
+ " <td>0.0</td>\n",
1798
+ " <td>0.0</td>\n",
1799
+ " </tr>\n",
1800
+ " <tr>\n",
1801
+ " <th>2024-08-19</th>\n",
1802
+ " <td>70.099998</td>\n",
1803
+ " <td>70.400002</td>\n",
1804
+ " <td>68.870003</td>\n",
1805
+ " <td>69.029999</td>\n",
1806
+ " <td>29067</td>\n",
1807
+ " <td>0.0</td>\n",
1808
+ " <td>0.0</td>\n",
1809
+ " </tr>\n",
1810
+ " <tr>\n",
1811
+ " <th>2024-08-20</th>\n",
1812
+ " <td>69.150002</td>\n",
1813
+ " <td>69.250000</td>\n",
1814
+ " <td>68.309998</td>\n",
1815
+ " <td>68.389999</td>\n",
1816
+ " <td>29827</td>\n",
1817
+ " <td>0.0</td>\n",
1818
+ " <td>0.0</td>\n",
1819
+ " </tr>\n",
1820
+ " <tr>\n",
1821
+ " <th>2024-08-21</th>\n",
1822
+ " <td>68.389999</td>\n",
1823
+ " <td>68.959999</td>\n",
1824
+ " <td>67.370003</td>\n",
1825
+ " <td>67.629997</td>\n",
1826
+ " <td>29827</td>\n",
1827
+ " <td>0.0</td>\n",
1828
+ " <td>0.0</td>\n",
1829
+ " </tr>\n",
1830
+ " <tr>\n",
1831
+ " <th>2024-08-22</th>\n",
1832
+ " <td>67.730003</td>\n",
1833
+ " <td>68.650002</td>\n",
1834
+ " <td>67.430000</td>\n",
1835
+ " <td>68.220001</td>\n",
1836
+ " <td>33722</td>\n",
1837
+ " <td>0.0</td>\n",
1838
+ " <td>0.0</td>\n",
1839
+ " </tr>\n",
1840
+ " </tbody>\n",
1841
+ "</table>\n",
1842
+ "<p>254 rows × 7 columns</p>\n",
1843
+ "</div>"
1844
+ ],
1845
+ "text/plain": [
1846
+ " open high low close volume split_ratio \\\n",
1847
+ "date \n",
1848
+ "2023-08-22 71.419998 71.949997 71.029999 71.160004 5342 0.0 \n",
1849
+ "2023-08-23 71.120003 71.320000 69.709999 70.730003 5139 0.0 \n",
1850
+ "2023-08-24 70.459999 70.860001 69.870003 70.190002 7594 0.0 \n",
1851
+ "2023-08-25 70.050003 70.889999 69.470001 70.680000 9328 0.0 \n",
1852
+ "2023-08-28 70.690002 71.190002 70.239998 70.480003 6234 0.0 \n",
1853
+ "... ... ... ... ... ... ... \n",
1854
+ "2024-08-16 70.940002 70.989998 69.440002 70.019997 38165 0.0 \n",
1855
+ "2024-08-19 70.099998 70.400002 68.870003 69.029999 29067 0.0 \n",
1856
+ "2024-08-20 69.150002 69.250000 68.309998 68.389999 29827 0.0 \n",
1857
+ "2024-08-21 68.389999 68.959999 67.370003 67.629997 29827 0.0 \n",
1858
+ "2024-08-22 67.730003 68.650002 67.430000 68.220001 33722 0.0 \n",
1859
+ "\n",
1860
+ " dividend \n",
1861
+ "date \n",
1862
+ "2023-08-22 0.0 \n",
1863
+ "2023-08-23 0.0 \n",
1864
+ "2023-08-24 0.0 \n",
1865
+ "2023-08-25 0.0 \n",
1866
+ "2023-08-28 0.0 \n",
1867
+ "... ... \n",
1868
+ "2024-08-16 0.0 \n",
1869
+ "2024-08-19 0.0 \n",
1870
+ "2024-08-20 0.0 \n",
1871
+ "2024-08-21 0.0 \n",
1872
+ "2024-08-22 0.0 \n",
1873
+ "\n",
1874
+ "[254 rows x 7 columns]"
1875
+ ]
1876
+ },
1877
+ "execution_count": 19,
1878
+ "metadata": {},
1879
+ "output_type": "execute_result"
1880
+ }
1881
+ ],
1882
+ "source": [
1883
+ "obb.equity.price.historical(\"CLZ25.NYM\", provider=\"yfinance\").to_df()"
1884
+ ]
1885
+ },
1886
+ {
1887
+ "cell_type": "code",
1888
+ "execution_count": 20,
1889
+ "metadata": {},
1890
+ "outputs": [
1891
+ {
1892
+ "data": {
1893
+ "text/html": [
1894
+ "<div>\n",
1895
+ "<style scoped>\n",
1896
+ " .dataframe tbody tr th:only-of-type {\n",
1897
+ " vertical-align: middle;\n",
1898
+ " }\n",
1899
+ "\n",
1900
+ " .dataframe tbody tr th {\n",
1901
+ " vertical-align: top;\n",
1902
+ " }\n",
1903
+ "\n",
1904
+ " .dataframe thead th {\n",
1905
+ " text-align: right;\n",
1906
+ " }\n",
1907
+ "</style>\n",
1908
+ "<table border=\"1\" class=\"dataframe\">\n",
1909
+ " <thead>\n",
1910
+ " <tr style=\"text-align: right;\">\n",
1911
+ " <th></th>\n",
1912
+ " <th>open</th>\n",
1913
+ " <th>high</th>\n",
1914
+ " <th>low</th>\n",
1915
+ " <th>close</th>\n",
1916
+ " <th>volume</th>\n",
1917
+ " <th>vwap</th>\n",
1918
+ " <th>adj_close</th>\n",
1919
+ " <th>unadjusted_volume</th>\n",
1920
+ " <th>change</th>\n",
1921
+ " <th>change_percent</th>\n",
1922
+ " </tr>\n",
1923
+ " <tr>\n",
1924
+ " <th>date</th>\n",
1925
+ " <th></th>\n",
1926
+ " <th></th>\n",
1927
+ " <th></th>\n",
1928
+ " <th></th>\n",
1929
+ " <th></th>\n",
1930
+ " <th></th>\n",
1931
+ " <th></th>\n",
1932
+ " <th></th>\n",
1933
+ " <th></th>\n",
1934
+ " <th></th>\n",
1935
+ " </tr>\n",
1936
+ " </thead>\n",
1937
+ " <tbody>\n",
1938
+ " <tr>\n",
1939
+ " <th>2023-08-22</th>\n",
1940
+ " <td>80.80</td>\n",
1941
+ " <td>80.99</td>\n",
1942
+ " <td>80.10</td>\n",
1943
+ " <td>80.35</td>\n",
1944
+ " <td>287489</td>\n",
1945
+ " <td>80.48</td>\n",
1946
+ " <td>80.35</td>\n",
1947
+ " <td>287489.0</td>\n",
1948
+ " <td>-0.45</td>\n",
1949
+ " <td>-0.005569</td>\n",
1950
+ " </tr>\n",
1951
+ " <tr>\n",
1952
+ " <th>2023-08-23</th>\n",
1953
+ " <td>79.64</td>\n",
1954
+ " <td>79.91</td>\n",
1955
+ " <td>77.62</td>\n",
1956
+ " <td>78.89</td>\n",
1957
+ " <td>378146</td>\n",
1958
+ " <td>78.81</td>\n",
1959
+ " <td>78.89</td>\n",
1960
+ " <td>378146.0</td>\n",
1961
+ " <td>-0.75</td>\n",
1962
+ " <td>-0.009417</td>\n",
1963
+ " </tr>\n",
1964
+ " <tr>\n",
1965
+ " <th>2023-08-24</th>\n",
1966
+ " <td>78.57</td>\n",
1967
+ " <td>79.28</td>\n",
1968
+ " <td>77.59</td>\n",
1969
+ " <td>79.05</td>\n",
1970
+ " <td>349230</td>\n",
1971
+ " <td>78.64</td>\n",
1972
+ " <td>79.05</td>\n",
1973
+ " <td>349230.0</td>\n",
1974
+ " <td>0.48</td>\n",
1975
+ " <td>0.006109</td>\n",
1976
+ " </tr>\n",
1977
+ " <tr>\n",
1978
+ " <th>2023-08-25</th>\n",
1979
+ " <td>78.88</td>\n",
1980
+ " <td>80.45</td>\n",
1981
+ " <td>78.14</td>\n",
1982
+ " <td>79.83</td>\n",
1983
+ " <td>411409</td>\n",
1984
+ " <td>79.47</td>\n",
1985
+ " <td>79.83</td>\n",
1986
+ " <td>411409.0</td>\n",
1987
+ " <td>0.95</td>\n",
1988
+ " <td>0.012000</td>\n",
1989
+ " </tr>\n",
1990
+ " <tr>\n",
1991
+ " <th>2023-08-28</th>\n",
1992
+ " <td>80.15</td>\n",
1993
+ " <td>80.87</td>\n",
1994
+ " <td>79.61</td>\n",
1995
+ " <td>80.10</td>\n",
1996
+ " <td>246584</td>\n",
1997
+ " <td>80.19</td>\n",
1998
+ " <td>80.10</td>\n",
1999
+ " <td>246584.0</td>\n",
2000
+ " <td>-0.05</td>\n",
2001
+ " <td>-0.000624</td>\n",
2002
+ " </tr>\n",
2003
+ " <tr>\n",
2004
+ " <th>...</th>\n",
2005
+ " <td>...</td>\n",
2006
+ " <td>...</td>\n",
2007
+ " <td>...</td>\n",
2008
+ " <td>...</td>\n",
2009
+ " <td>...</td>\n",
2010
+ " <td>...</td>\n",
2011
+ " <td>...</td>\n",
2012
+ " <td>...</td>\n",
2013
+ " <td>...</td>\n",
2014
+ " <td>...</td>\n",
2015
+ " </tr>\n",
2016
+ " <tr>\n",
2017
+ " <th>2024-08-18</th>\n",
2018
+ " <td>76.58</td>\n",
2019
+ " <td>76.71</td>\n",
2020
+ " <td>76.48</td>\n",
2021
+ " <td>76.71</td>\n",
2022
+ " <td>175</td>\n",
2023
+ " <td>76.62</td>\n",
2024
+ " <td>76.71</td>\n",
2025
+ " <td>175.0</td>\n",
2026
+ " <td>0.13</td>\n",
2027
+ " <td>0.001698</td>\n",
2028
+ " </tr>\n",
2029
+ " <tr>\n",
2030
+ " <th>2024-08-19</th>\n",
2031
+ " <td>76.58</td>\n",
2032
+ " <td>76.87</td>\n",
2033
+ " <td>74.17</td>\n",
2034
+ " <td>74.37</td>\n",
2035
+ " <td>118172</td>\n",
2036
+ " <td>75.50</td>\n",
2037
+ " <td>74.37</td>\n",
2038
+ " <td>118172.0</td>\n",
2039
+ " <td>-2.21</td>\n",
2040
+ " <td>-0.028900</td>\n",
2041
+ " </tr>\n",
2042
+ " <tr>\n",
2043
+ " <th>2024-08-20</th>\n",
2044
+ " <td>74.34</td>\n",
2045
+ " <td>75.03</td>\n",
2046
+ " <td>73.50</td>\n",
2047
+ " <td>74.04</td>\n",
2048
+ " <td>118172</td>\n",
2049
+ " <td>74.23</td>\n",
2050
+ " <td>74.04</td>\n",
2051
+ " <td>118172.0</td>\n",
2052
+ " <td>-0.30</td>\n",
2053
+ " <td>-0.004036</td>\n",
2054
+ " </tr>\n",
2055
+ " <tr>\n",
2056
+ " <th>2024-08-21</th>\n",
2057
+ " <td>73.12</td>\n",
2058
+ " <td>74.16</td>\n",
2059
+ " <td>71.46</td>\n",
2060
+ " <td>71.93</td>\n",
2061
+ " <td>361850</td>\n",
2062
+ " <td>72.67</td>\n",
2063
+ " <td>71.93</td>\n",
2064
+ " <td>361850.0</td>\n",
2065
+ " <td>-1.19</td>\n",
2066
+ " <td>-0.016300</td>\n",
2067
+ " </tr>\n",
2068
+ " <tr>\n",
2069
+ " <th>2024-08-22</th>\n",
2070
+ " <td>71.93</td>\n",
2071
+ " <td>73.52</td>\n",
2072
+ " <td>71.58</td>\n",
2073
+ " <td>73.00</td>\n",
2074
+ " <td>28663</td>\n",
2075
+ " <td>72.70</td>\n",
2076
+ " <td>73.00</td>\n",
2077
+ " <td>28663.0</td>\n",
2078
+ " <td>1.07</td>\n",
2079
+ " <td>0.014876</td>\n",
2080
+ " </tr>\n",
2081
+ " </tbody>\n",
2082
+ "</table>\n",
2083
+ "<p>266 rows × 10 columns</p>\n",
2084
+ "</div>"
2085
+ ],
2086
+ "text/plain": [
2087
+ " open high low close volume vwap adj_close \\\n",
2088
+ "date \n",
2089
+ "2023-08-22 80.80 80.99 80.10 80.35 287489 80.48 80.35 \n",
2090
+ "2023-08-23 79.64 79.91 77.62 78.89 378146 78.81 78.89 \n",
2091
+ "2023-08-24 78.57 79.28 77.59 79.05 349230 78.64 79.05 \n",
2092
+ "2023-08-25 78.88 80.45 78.14 79.83 411409 79.47 79.83 \n",
2093
+ "2023-08-28 80.15 80.87 79.61 80.10 246584 80.19 80.10 \n",
2094
+ "... ... ... ... ... ... ... ... \n",
2095
+ "2024-08-18 76.58 76.71 76.48 76.71 175 76.62 76.71 \n",
2096
+ "2024-08-19 76.58 76.87 74.17 74.37 118172 75.50 74.37 \n",
2097
+ "2024-08-20 74.34 75.03 73.50 74.04 118172 74.23 74.04 \n",
2098
+ "2024-08-21 73.12 74.16 71.46 71.93 361850 72.67 71.93 \n",
2099
+ "2024-08-22 71.93 73.52 71.58 73.00 28663 72.70 73.00 \n",
2100
+ "\n",
2101
+ " unadjusted_volume change change_percent \n",
2102
+ "date \n",
2103
+ "2023-08-22 287489.0 -0.45 -0.005569 \n",
2104
+ "2023-08-23 378146.0 -0.75 -0.009417 \n",
2105
+ "2023-08-24 349230.0 0.48 0.006109 \n",
2106
+ "2023-08-25 411409.0 0.95 0.012000 \n",
2107
+ "2023-08-28 246584.0 -0.05 -0.000624 \n",
2108
+ "... ... ... ... \n",
2109
+ "2024-08-18 175.0 0.13 0.001698 \n",
2110
+ "2024-08-19 118172.0 -2.21 -0.028900 \n",
2111
+ "2024-08-20 118172.0 -0.30 -0.004036 \n",
2112
+ "2024-08-21 361850.0 -1.19 -0.016300 \n",
2113
+ "2024-08-22 28663.0 1.07 0.014876 \n",
2114
+ "\n",
2115
+ "[266 rows x 10 columns]"
2116
+ ]
2117
+ },
2118
+ "execution_count": 20,
2119
+ "metadata": {},
2120
+ "output_type": "execute_result"
2121
+ }
2122
+ ],
2123
+ "source": [
2124
+ "obb.equity.price.historical(\"CL=F\", provider=\"fmp\").to_df()"
2125
+ ]
2126
+ },
2127
+ {
2128
+ "cell_type": "code",
2129
+ "execution_count": 21,
2130
+ "metadata": {},
2131
+ "outputs": [
2132
+ {
2133
+ "data": {
2134
+ "text/html": [
2135
+ "<div>\n",
2136
+ "<style scoped>\n",
2137
+ " .dataframe tbody tr th:only-of-type {\n",
2138
+ " vertical-align: middle;\n",
2139
+ " }\n",
2140
+ "\n",
2141
+ " .dataframe tbody tr th {\n",
2142
+ " vertical-align: top;\n",
2143
+ " }\n",
2144
+ "\n",
2145
+ " .dataframe thead th {\n",
2146
+ " text-align: right;\n",
2147
+ " }\n",
2148
+ "</style>\n",
2149
+ "<table border=\"1\" class=\"dataframe\">\n",
2150
+ " <thead>\n",
2151
+ " <tr style=\"text-align: right;\">\n",
2152
+ " <th></th>\n",
2153
+ " <th>open</th>\n",
2154
+ " <th>high</th>\n",
2155
+ " <th>low</th>\n",
2156
+ " <th>close</th>\n",
2157
+ " <th>volume</th>\n",
2158
+ " <th>split_ratio</th>\n",
2159
+ " <th>dividend</th>\n",
2160
+ " </tr>\n",
2161
+ " <tr>\n",
2162
+ " <th>date</th>\n",
2163
+ " <th></th>\n",
2164
+ " <th></th>\n",
2165
+ " <th></th>\n",
2166
+ " <th></th>\n",
2167
+ " <th></th>\n",
2168
+ " <th></th>\n",
2169
+ " <th></th>\n",
2170
+ " </tr>\n",
2171
+ " </thead>\n",
2172
+ " <tbody>\n",
2173
+ " <tr>\n",
2174
+ " <th>2023-08-22</th>\n",
2175
+ " <td>146.238007</td>\n",
2176
+ " <td>146.389999</td>\n",
2177
+ " <td>145.501999</td>\n",
2178
+ " <td>146.238007</td>\n",
2179
+ " <td>0</td>\n",
2180
+ " <td>0.0</td>\n",
2181
+ " <td>0.0</td>\n",
2182
+ " </tr>\n",
2183
+ " <tr>\n",
2184
+ " <th>2023-08-23</th>\n",
2185
+ " <td>145.763000</td>\n",
2186
+ " <td>145.813004</td>\n",
2187
+ " <td>144.580002</td>\n",
2188
+ " <td>145.763000</td>\n",
2189
+ " <td>0</td>\n",
2190
+ " <td>0.0</td>\n",
2191
+ " <td>0.0</td>\n",
2192
+ " </tr>\n",
2193
+ " <tr>\n",
2194
+ " <th>2023-08-24</th>\n",
2195
+ " <td>144.673004</td>\n",
2196
+ " <td>145.947006</td>\n",
2197
+ " <td>144.621002</td>\n",
2198
+ " <td>144.673004</td>\n",
2199
+ " <td>0</td>\n",
2200
+ " <td>0.0</td>\n",
2201
+ " <td>0.0</td>\n",
2202
+ " </tr>\n",
2203
+ " <tr>\n",
2204
+ " <th>2023-08-25</th>\n",
2205
+ " <td>146.067001</td>\n",
2206
+ " <td>146.604996</td>\n",
2207
+ " <td>145.733994</td>\n",
2208
+ " <td>146.067001</td>\n",
2209
+ " <td>0</td>\n",
2210
+ " <td>0.0</td>\n",
2211
+ " <td>0.0</td>\n",
2212
+ " </tr>\n",
2213
+ " <tr>\n",
2214
+ " <th>2023-08-28</th>\n",
2215
+ " <td>146.531006</td>\n",
2216
+ " <td>146.716003</td>\n",
2217
+ " <td>146.278000</td>\n",
2218
+ " <td>146.531006</td>\n",
2219
+ " <td>0</td>\n",
2220
+ " <td>0.0</td>\n",
2221
+ " <td>0.0</td>\n",
2222
+ " </tr>\n",
2223
+ " <tr>\n",
2224
+ " <th>...</th>\n",
2225
+ " <td>...</td>\n",
2226
+ " <td>...</td>\n",
2227
+ " <td>...</td>\n",
2228
+ " <td>...</td>\n",
2229
+ " <td>...</td>\n",
2230
+ " <td>...</td>\n",
2231
+ " <td>...</td>\n",
2232
+ " </tr>\n",
2233
+ " <tr>\n",
2234
+ " <th>2024-08-16</th>\n",
2235
+ " <td>149.222000</td>\n",
2236
+ " <td>149.229996</td>\n",
2237
+ " <td>147.639008</td>\n",
2238
+ " <td>149.222000</td>\n",
2239
+ " <td>0</td>\n",
2240
+ " <td>0.0</td>\n",
2241
+ " <td>0.0</td>\n",
2242
+ " </tr>\n",
2243
+ " <tr>\n",
2244
+ " <th>2024-08-19</th>\n",
2245
+ " <td>147.955994</td>\n",
2246
+ " <td>147.959000</td>\n",
2247
+ " <td>145.220993</td>\n",
2248
+ " <td>147.955994</td>\n",
2249
+ " <td>0</td>\n",
2250
+ " <td>0.0</td>\n",
2251
+ " <td>0.0</td>\n",
2252
+ " </tr>\n",
2253
+ " <tr>\n",
2254
+ " <th>2024-08-20</th>\n",
2255
+ " <td>146.699005</td>\n",
2256
+ " <td>147.319000</td>\n",
2257
+ " <td>145.533997</td>\n",
2258
+ " <td>146.699005</td>\n",
2259
+ " <td>0</td>\n",
2260
+ " <td>0.0</td>\n",
2261
+ " <td>0.0</td>\n",
2262
+ " </tr>\n",
2263
+ " <tr>\n",
2264
+ " <th>2024-08-21</th>\n",
2265
+ " <td>145.347000</td>\n",
2266
+ " <td>146.339005</td>\n",
2267
+ " <td>144.981003</td>\n",
2268
+ " <td>145.347000</td>\n",
2269
+ " <td>0</td>\n",
2270
+ " <td>0.0</td>\n",
2271
+ " <td>0.0</td>\n",
2272
+ " </tr>\n",
2273
+ " <tr>\n",
2274
+ " <th>2024-08-22</th>\n",
2275
+ " <td>145.117996</td>\n",
2276
+ " <td>146.524994</td>\n",
2277
+ " <td>144.839996</td>\n",
2278
+ " <td>146.292999</td>\n",
2279
+ " <td>0</td>\n",
2280
+ " <td>0.0</td>\n",
2281
+ " <td>0.0</td>\n",
2282
+ " </tr>\n",
2283
+ " </tbody>\n",
2284
+ "</table>\n",
2285
+ "<p>262 rows × 7 columns</p>\n",
2286
+ "</div>"
2287
+ ],
2288
+ "text/plain": [
2289
+ " open high low close volume \\\n",
2290
+ "date \n",
2291
+ "2023-08-22 146.238007 146.389999 145.501999 146.238007 0 \n",
2292
+ "2023-08-23 145.763000 145.813004 144.580002 145.763000 0 \n",
2293
+ "2023-08-24 144.673004 145.947006 144.621002 144.673004 0 \n",
2294
+ "2023-08-25 146.067001 146.604996 145.733994 146.067001 0 \n",
2295
+ "2023-08-28 146.531006 146.716003 146.278000 146.531006 0 \n",
2296
+ "... ... ... ... ... ... \n",
2297
+ "2024-08-16 149.222000 149.229996 147.639008 149.222000 0 \n",
2298
+ "2024-08-19 147.955994 147.959000 145.220993 147.955994 0 \n",
2299
+ "2024-08-20 146.699005 147.319000 145.533997 146.699005 0 \n",
2300
+ "2024-08-21 145.347000 146.339005 144.981003 145.347000 0 \n",
2301
+ "2024-08-22 145.117996 146.524994 144.839996 146.292999 0 \n",
2302
+ "\n",
2303
+ " split_ratio dividend \n",
2304
+ "date \n",
2305
+ "2023-08-22 0.0 0.0 \n",
2306
+ "2023-08-23 0.0 0.0 \n",
2307
+ "2023-08-24 0.0 0.0 \n",
2308
+ "2023-08-25 0.0 0.0 \n",
2309
+ "2023-08-28 0.0 0.0 \n",
2310
+ "... ... ... \n",
2311
+ "2024-08-16 0.0 0.0 \n",
2312
+ "2024-08-19 0.0 0.0 \n",
2313
+ "2024-08-20 0.0 0.0 \n",
2314
+ "2024-08-21 0.0 0.0 \n",
2315
+ "2024-08-22 0.0 0.0 \n",
2316
+ "\n",
2317
+ "[262 rows x 7 columns]"
2318
+ ]
2319
+ },
2320
+ "execution_count": 21,
2321
+ "metadata": {},
2322
+ "output_type": "execute_result"
2323
+ }
2324
+ ],
2325
+ "source": [
2326
+ "obb.equity.price.historical(\"usdjpy=x\", provider=\"yfinance\").to_df()"
2327
+ ]
2328
+ },
2329
+ {
2330
+ "cell_type": "code",
2331
+ "execution_count": 22,
2332
+ "metadata": {},
2333
+ "outputs": [
2334
+ {
2335
+ "data": {
2336
+ "text/html": [
2337
+ "<div>\n",
2338
+ "<style scoped>\n",
2339
+ " .dataframe tbody tr th:only-of-type {\n",
2340
+ " vertical-align: middle;\n",
2341
+ " }\n",
2342
+ "\n",
2343
+ " .dataframe tbody tr th {\n",
2344
+ " vertical-align: top;\n",
2345
+ " }\n",
2346
+ "\n",
2347
+ " .dataframe thead th {\n",
2348
+ " text-align: right;\n",
2349
+ " }\n",
2350
+ "</style>\n",
2351
+ "<table border=\"1\" class=\"dataframe\">\n",
2352
+ " <thead>\n",
2353
+ " <tr style=\"text-align: right;\">\n",
2354
+ " <th></th>\n",
2355
+ " <th>open</th>\n",
2356
+ " <th>high</th>\n",
2357
+ " <th>low</th>\n",
2358
+ " <th>close</th>\n",
2359
+ " <th>volume</th>\n",
2360
+ " </tr>\n",
2361
+ " <tr>\n",
2362
+ " <th>date</th>\n",
2363
+ " <th></th>\n",
2364
+ " <th></th>\n",
2365
+ " <th></th>\n",
2366
+ " <th></th>\n",
2367
+ " <th></th>\n",
2368
+ " </tr>\n",
2369
+ " </thead>\n",
2370
+ " <tbody>\n",
2371
+ " <tr>\n",
2372
+ " <th>2023-08-22</th>\n",
2373
+ " <td>146.238007</td>\n",
2374
+ " <td>146.389999</td>\n",
2375
+ " <td>145.501999</td>\n",
2376
+ " <td>146.238007</td>\n",
2377
+ " <td>0.0</td>\n",
2378
+ " </tr>\n",
2379
+ " <tr>\n",
2380
+ " <th>2023-08-23</th>\n",
2381
+ " <td>145.763000</td>\n",
2382
+ " <td>145.813004</td>\n",
2383
+ " <td>144.580002</td>\n",
2384
+ " <td>145.763000</td>\n",
2385
+ " <td>0.0</td>\n",
2386
+ " </tr>\n",
2387
+ " <tr>\n",
2388
+ " <th>2023-08-24</th>\n",
2389
+ " <td>144.673004</td>\n",
2390
+ " <td>145.947006</td>\n",
2391
+ " <td>144.621002</td>\n",
2392
+ " <td>144.673004</td>\n",
2393
+ " <td>0.0</td>\n",
2394
+ " </tr>\n",
2395
+ " <tr>\n",
2396
+ " <th>2023-08-25</th>\n",
2397
+ " <td>146.067001</td>\n",
2398
+ " <td>146.604996</td>\n",
2399
+ " <td>145.733994</td>\n",
2400
+ " <td>146.067001</td>\n",
2401
+ " <td>0.0</td>\n",
2402
+ " </tr>\n",
2403
+ " <tr>\n",
2404
+ " <th>2023-08-28</th>\n",
2405
+ " <td>146.531006</td>\n",
2406
+ " <td>146.716003</td>\n",
2407
+ " <td>146.278000</td>\n",
2408
+ " <td>146.531006</td>\n",
2409
+ " <td>0.0</td>\n",
2410
+ " </tr>\n",
2411
+ " <tr>\n",
2412
+ " <th>...</th>\n",
2413
+ " <td>...</td>\n",
2414
+ " <td>...</td>\n",
2415
+ " <td>...</td>\n",
2416
+ " <td>...</td>\n",
2417
+ " <td>...</td>\n",
2418
+ " </tr>\n",
2419
+ " <tr>\n",
2420
+ " <th>2024-08-16</th>\n",
2421
+ " <td>149.222000</td>\n",
2422
+ " <td>149.229996</td>\n",
2423
+ " <td>147.639008</td>\n",
2424
+ " <td>149.222000</td>\n",
2425
+ " <td>0.0</td>\n",
2426
+ " </tr>\n",
2427
+ " <tr>\n",
2428
+ " <th>2024-08-19</th>\n",
2429
+ " <td>147.955994</td>\n",
2430
+ " <td>147.959000</td>\n",
2431
+ " <td>145.220993</td>\n",
2432
+ " <td>147.955994</td>\n",
2433
+ " <td>0.0</td>\n",
2434
+ " </tr>\n",
2435
+ " <tr>\n",
2436
+ " <th>2024-08-20</th>\n",
2437
+ " <td>146.699005</td>\n",
2438
+ " <td>147.319000</td>\n",
2439
+ " <td>145.533997</td>\n",
2440
+ " <td>146.699005</td>\n",
2441
+ " <td>0.0</td>\n",
2442
+ " </tr>\n",
2443
+ " <tr>\n",
2444
+ " <th>2024-08-21</th>\n",
2445
+ " <td>145.347000</td>\n",
2446
+ " <td>146.339005</td>\n",
2447
+ " <td>144.981003</td>\n",
2448
+ " <td>145.347000</td>\n",
2449
+ " <td>0.0</td>\n",
2450
+ " </tr>\n",
2451
+ " <tr>\n",
2452
+ " <th>2024-08-22</th>\n",
2453
+ " <td>145.117996</td>\n",
2454
+ " <td>146.524994</td>\n",
2455
+ " <td>144.839996</td>\n",
2456
+ " <td>146.287003</td>\n",
2457
+ " <td>0.0</td>\n",
2458
+ " </tr>\n",
2459
+ " </tbody>\n",
2460
+ "</table>\n",
2461
+ "<p>262 rows × 5 columns</p>\n",
2462
+ "</div>"
2463
+ ],
2464
+ "text/plain": [
2465
+ " open high low close volume\n",
2466
+ "date \n",
2467
+ "2023-08-22 146.238007 146.389999 145.501999 146.238007 0.0\n",
2468
+ "2023-08-23 145.763000 145.813004 144.580002 145.763000 0.0\n",
2469
+ "2023-08-24 144.673004 145.947006 144.621002 144.673004 0.0\n",
2470
+ "2023-08-25 146.067001 146.604996 145.733994 146.067001 0.0\n",
2471
+ "2023-08-28 146.531006 146.716003 146.278000 146.531006 0.0\n",
2472
+ "... ... ... ... ... ...\n",
2473
+ "2024-08-16 149.222000 149.229996 147.639008 149.222000 0.0\n",
2474
+ "2024-08-19 147.955994 147.959000 145.220993 147.955994 0.0\n",
2475
+ "2024-08-20 146.699005 147.319000 145.533997 146.699005 0.0\n",
2476
+ "2024-08-21 145.347000 146.339005 144.981003 145.347000 0.0\n",
2477
+ "2024-08-22 145.117996 146.524994 144.839996 146.287003 0.0\n",
2478
+ "\n",
2479
+ "[262 rows x 5 columns]"
2480
+ ]
2481
+ },
2482
+ "execution_count": 22,
2483
+ "metadata": {},
2484
+ "output_type": "execute_result"
2485
+ }
2486
+ ],
2487
+ "source": [
2488
+ "obb.currency.price.historical(\"usdjpy\", provider=\"yfinance\").to_df()"
2489
+ ]
2490
+ }
2491
+ ],
2492
+ "metadata": {
2493
+ "kernelspec": {
2494
+ "display_name": "obb",
2495
+ "language": "python",
2496
+ "name": "python3"
2497
+ },
2498
+ "language_info": {
2499
+ "codemirror_mode": {
2500
+ "name": "ipython",
2501
+ "version": 3
2502
+ },
2503
+ "file_extension": ".py",
2504
+ "mimetype": "text/x-python",
2505
+ "name": "python",
2506
+ "nbconvert_exporter": "python",
2507
+ "pygments_lexer": "ipython3",
2508
+ "version": "3.12.4"
2509
+ },
2510
+ "orig_nbformat": 4
2511
+ },
2512
+ "nbformat": 4,
2513
+ "nbformat_minor": 2
2514
+ }
examples/mAndAImpact.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
examples/openbb-apachebeam/README.md ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # OBB Dataflow Sample
2
+
3
+
4
+ This is a sample how to invoke OBB fetchers in an Apache Beam pipeline. (GCP dataflow is build on Apache Beam)
5
+ Pre-requisites
6
+ - You need to create a Conda environment (or a virtual env) using requirements.txt in this directory
7
+ - The script exercise 3 OBB endpoints, all of which require no credentials
8
+ - Run the test from the main directory
9
+ python -m unittest .\tests\test_obb_pipeline.py
10
+
11
+ The script will run a pipeline consisting of 3 task which will fetch AAPL quote, profile and news
12
+ This is just a very basic sample which can be used as building block to create more complex scenarios
examples/openbb-apachebeam/requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ apache-beam
2
+ openbb-yfinance
examples/openbb-apachebeam/tests/__init__.py ADDED
File without changes
examples/openbb-apachebeam/tests/test_obb_pipeline.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import unittest
2
+ from apache_beam.testing.test_pipeline import TestPipeline
3
+ from apache_beam.options.pipeline_options import PipelineOptions
4
+ import asyncio
5
+ import apache_beam as beam
6
+ from openbb_yfinance.models.equity_quote import YFinanceEquityQuoteFetcher as quote_fetcher
7
+ from openbb_yfinance.models.equity_profile import YFinanceEquityProfileFetcher as profile_fetcher
8
+ from openbb_yfinance.models.company_news import YFinanceCompanyNewsFetcher as news_fetcher
9
+
10
+
11
+ class AsyncProcess(beam.DoFn):
12
+
13
+ def __init__(self, credentials, fetcher):
14
+ self.credentials = credentials
15
+ self.fetcher = fetcher
16
+
17
+ async def fetch_data(self, element: str):
18
+ params = dict(symbol=element)
19
+ data = await self.fetcher.fetch_data(params, self.credentials)
20
+ return [d.model_dump(exclude_none=True) for d in data]
21
+
22
+ def process(self, element: str):
23
+ return asyncio.run(self.fetch_data(element))
24
+
25
+ class MyTestCase(unittest.TestCase):
26
+
27
+
28
+ def test_sample_pipeline(self):
29
+ credentials = {} # Running OBB endpoints which do not require credentials
30
+ debug_sink = beam.Map(print)
31
+ ticker = 'AAPL'
32
+
33
+ with TestPipeline(options=PipelineOptions()) as p:
34
+ quote = (p | 'Start Quote' >> beam.Create([ticker])
35
+ | 'Run Quote' >> beam.ParDo(AsyncProcess(credentials, quote_fetcher))
36
+ | 'Print quote' >> debug_sink)
37
+
38
+ profile = (p | 'Start Profile' >> beam.Create([ticker])
39
+ | 'Run Profile' >> beam.ParDo(AsyncProcess(credentials, profile_fetcher))
40
+ | 'Print profile' >> debug_sink)
41
+
42
+ news = (p | 'Start News' >> beam.Create([ticker])
43
+ | 'Run News' >> beam.ParDo(AsyncProcess(credentials, news_fetcher))
44
+ | 'Print nes' >> debug_sink)
45
+
46
+
47
+ if __name__ == '__main__':
48
+ unittest.main()
examples/openbbPlatformAsLLMTools.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
examples/platform_standardization.ipynb ADDED
@@ -0,0 +1,761 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# How The OpenBB Platform Works"
8
+ ]
9
+ },
10
+ {
11
+ "cell_type": "code",
12
+ "execution_count": 1,
13
+ "metadata": {},
14
+ "outputs": [],
15
+ "source": [
16
+ "from openbb import obb"
17
+ ]
18
+ },
19
+ {
20
+ "cell_type": "code",
21
+ "execution_count": null,
22
+ "metadata": {},
23
+ "outputs": [],
24
+ "source": [
25
+ "obb"
26
+ ]
27
+ },
28
+ {
29
+ "cell_type": "code",
30
+ "execution_count": 3,
31
+ "metadata": {},
32
+ "outputs": [
33
+ {
34
+ "data": {
35
+ "text/plain": [
36
+ "/news\n",
37
+ " company\n",
38
+ " world\n",
39
+ " "
40
+ ]
41
+ },
42
+ "execution_count": 3,
43
+ "metadata": {},
44
+ "output_type": "execute_result"
45
+ }
46
+ ],
47
+ "source": [
48
+ "obb.news"
49
+ ]
50
+ },
51
+ {
52
+ "cell_type": "code",
53
+ "execution_count": 4,
54
+ "metadata": {},
55
+ "outputs": [
56
+ {
57
+ "name": "stdout",
58
+ "output_type": "stream",
59
+ "text": [
60
+ "Help on method world in module openbb.package.news:\n",
61
+ "\n",
62
+ "world(limit: Annotated[int, OpenBBField(description='The number of data entries to return. The number of articles to return.')] = 2500, start_date: Annotated[Union[datetime.date, NoneType, str], OpenBBField(description='Start date of the data, in YYYY-MM-DD format.')] = None, end_date: Annotated[Union[datetime.date, NoneType, str], OpenBBField(description='End date of the data, in YYYY-MM-DD format.')] = None, provider: Annotated[Optional[Literal['benzinga', 'biztoc', 'fmp', 'intrinio', 'tiingo']], OpenBBField(description='The provider to use, by default None. If None, the priority list configured in the settings is used. Default priority: benzinga, biztoc, fmp, intrinio, tiingo.')] = None, **kwargs) -> openbb_core.app.model.obbject.OBBject method of openbb.package.news.ROUTER_news instance\n",
63
+ " World News. Global news data.\n",
64
+ "\n",
65
+ " Parameters\n",
66
+ " ----------\n",
67
+ " limit : int\n",
68
+ " The number of data entries to return. The number of articles to return.\n",
69
+ " start_date : Union[date, None, str]\n",
70
+ " Start date of the data, in YYYY-MM-DD format.\n",
71
+ " end_date : Union[date, None, str]\n",
72
+ " End date of the data, in YYYY-MM-DD format.\n",
73
+ " provider : Optional[Literal['benzinga', 'biztoc', 'fmp', 'intrinio', 'tiingo']]\n",
74
+ " The provider to use, by default None. If None, the priority list configured in the settings is used. Default priority: benzinga, biztoc, fmp, intrinio, tiingo.\n",
75
+ " date : Optional[datetime.date]\n",
76
+ " A specific date to get data for. (provider: benzinga)\n",
77
+ " display : Literal['headline', 'abstract', 'full']\n",
78
+ " Specify headline only (headline), headline + teaser (abstract), or headline + full body (full). (provider: benzinga)\n",
79
+ " updated_since : Optional[int]\n",
80
+ " Number of seconds since the news was updated. (provider: benzinga)\n",
81
+ " published_since : Optional[int]\n",
82
+ " Number of seconds since the news was published. (provider: benzinga)\n",
83
+ " sort : Literal['id', 'created', 'updated']\n",
84
+ " Key to sort the news by. (provider: benzinga)\n",
85
+ " order : Literal['asc', 'desc']\n",
86
+ " Order to sort the news by. (provider: benzinga)\n",
87
+ " isin : Optional[str]\n",
88
+ " The ISIN of the news to retrieve. (provider: benzinga)\n",
89
+ " cusip : Optional[str]\n",
90
+ " The CUSIP of the news to retrieve. (provider: benzinga)\n",
91
+ " channels : Optional[str]\n",
92
+ " Channels of the news to retrieve. (provider: benzinga)\n",
93
+ " topics : Optional[str]\n",
94
+ " Topics of the news to retrieve. (provider: benzinga)\n",
95
+ " authors : Optional[str]\n",
96
+ " Authors of the news to retrieve. (provider: benzinga)\n",
97
+ " content_types : Optional[str]\n",
98
+ " Content types of the news to retrieve. (provider: benzinga)\n",
99
+ " term : Optional[str]\n",
100
+ " Search term to filter articles by. This overrides all other filters. (provider: biztoc)\n",
101
+ " source : Optional[Union[str, Literal['yahoo', 'moody', 'moody_us_news', 'moody_us_press_releases']]]\n",
102
+ " Filter by a specific publisher. Only valid when filter is set to source. (provider: biztoc);\n",
103
+ " The source of the news article. (provider: intrinio);\n",
104
+ " A comma-separated list of the domains requested. (provider: tiingo)\n",
105
+ " sentiment : Optional[Literal['positive', 'neutral', 'negative']]\n",
106
+ " Return news only from this source. (provider: intrinio)\n",
107
+ " language : Optional[str]\n",
108
+ " Filter by language. Unsupported for yahoo source. (provider: intrinio)\n",
109
+ " topic : Optional[str]\n",
110
+ " Filter by topic. Unsupported for yahoo source. (provider: intrinio)\n",
111
+ " word_count_greater_than : Optional[int]\n",
112
+ " News stories will have a word count greater than this value. Unsupported for yahoo source. (provider: intrinio)\n",
113
+ " word_count_less_than : Optional[int]\n",
114
+ " News stories will have a word count less than this value. Unsupported for yahoo source. (provider: intrinio)\n",
115
+ " is_spam : Optional[bool]\n",
116
+ " Filter whether it is marked as spam or not. Unsupported for yahoo source. (provider: intrinio)\n",
117
+ " business_relevance_greater_than : Optional[float]\n",
118
+ " News stories will have a business relevance score more than this value. Unsupported for yahoo source. Value is a decimal between 0 and 1. (provider: intrinio)\n",
119
+ " business_relevance_less_than : Optional[float]\n",
120
+ " News stories will have a business relevance score less than this value. Unsupported for yahoo source. Value is a decimal between 0 and 1. (provider: intrinio)\n",
121
+ " offset : Optional[int]\n",
122
+ " Page offset, used in conjunction with limit. (provider: tiingo)\n",
123
+ "\n",
124
+ " Returns\n",
125
+ " -------\n",
126
+ " OBBject\n",
127
+ " results : List[WorldNews]\n",
128
+ " Serializable results.\n",
129
+ " provider : Optional[Literal['benzinga', 'biztoc', 'fmp', 'intrinio', 'tiingo']]\n",
130
+ " Provider name.\n",
131
+ " warnings : Optional[List[Warning_]]\n",
132
+ " List of warnings.\n",
133
+ " chart : Optional[Chart]\n",
134
+ " Chart object.\n",
135
+ " extra : Dict[str, Any]\n",
136
+ " Extra info.\n",
137
+ "\n",
138
+ " WorldNews\n",
139
+ " ---------\n",
140
+ " date : datetime\n",
141
+ " The date of the data. The published date of the article.\n",
142
+ " title : str\n",
143
+ " Title of the article.\n",
144
+ " images : Optional[List[Dict[str, str]]]\n",
145
+ " Images associated with the article.\n",
146
+ " text : Optional[str]\n",
147
+ " Text/body of the article.\n",
148
+ " url : Optional[str]\n",
149
+ " URL to the article.\n",
150
+ " id : Optional[str]\n",
151
+ " Article ID. (provider: benzinga, intrinio)\n",
152
+ " author : Optional[str]\n",
153
+ " Author of the news. (provider: benzinga)\n",
154
+ " teaser : Optional[str]\n",
155
+ " Teaser of the news. (provider: benzinga)\n",
156
+ " channels : Optional[str]\n",
157
+ " Channels associated with the news. (provider: benzinga)\n",
158
+ " stocks : Optional[str]\n",
159
+ " Stocks associated with the news. (provider: benzinga)\n",
160
+ " tags : Optional[Union[str, List[str]]]\n",
161
+ " Tags associated with the news. (provider: benzinga, biztoc, tiingo)\n",
162
+ " updated : Optional[datetime]\n",
163
+ " Updated date of the news. (provider: benzinga)\n",
164
+ " score : Optional[float]\n",
165
+ " Search relevance score for the article. (provider: biztoc)\n",
166
+ " site : Optional[str]\n",
167
+ " News source. (provider: fmp, tiingo)\n",
168
+ " source : Optional[str]\n",
169
+ " The source of the news article. (provider: intrinio)\n",
170
+ " summary : Optional[str]\n",
171
+ " The summary of the news article. (provider: intrinio)\n",
172
+ " topics : Optional[str]\n",
173
+ " The topics related to the news article. (provider: intrinio)\n",
174
+ " word_count : Optional[int]\n",
175
+ " The word count of the news article. (provider: intrinio)\n",
176
+ " business_relevance : Optional[float]\n",
177
+ " How strongly correlated the news article is to the business (provider: intrinio)\n",
178
+ " sentiment : Optional[str]\n",
179
+ " The sentiment of the news article - i.e, negative, positive. (provider: intrinio)\n",
180
+ " sentiment_confidence : Optional[float]\n",
181
+ " The confidence score of the sentiment rating. (provider: intrinio)\n",
182
+ " language : Optional[str]\n",
183
+ " The language of the news article. (provider: intrinio)\n",
184
+ " spam : Optional[bool]\n",
185
+ " Whether the news article is spam. (provider: intrinio)\n",
186
+ " copyright : Optional[str]\n",
187
+ " The copyright notice of the news article. (provider: intrinio)\n",
188
+ " company : Optional[IntrinioCompany]\n",
189
+ " The Intrinio Company object. Contains details company reference data. (provider: intrinio)\n",
190
+ " security : Optional[IntrinioSecurity]\n",
191
+ " The Intrinio Security object. Contains the security details related to the news article. (provider: intrinio)\n",
192
+ " symbols : Optional[str]\n",
193
+ " Ticker tagged in the fetched news. (provider: tiingo)\n",
194
+ " article_id : Optional[int]\n",
195
+ " Unique ID of the news article. (provider: tiingo)\n",
196
+ " crawl_date : Optional[datetime]\n",
197
+ " Date the news article was crawled. (provider: tiingo)\n",
198
+ "\n",
199
+ " Examples\n",
200
+ " --------\n",
201
+ " >>> from openbb import obb\n",
202
+ " >>> obb.news.world(provider='fmp')\n",
203
+ " >>> obb.news.world(limit=100, provider='intrinio')\n",
204
+ " >>> # Get news on the specified dates.\n",
205
+ " >>> obb.news.world(start_date='2024-02-01', end_date='2024-02-07', provider='intrinio')\n",
206
+ " >>> # Display the headlines of the news.\n",
207
+ " >>> obb.news.world(display='headline', provider='benzinga')\n",
208
+ " >>> # Get news by topics.\n",
209
+ " >>> obb.news.world(topics='finance', provider='benzinga')\n",
210
+ " >>> # Get news by source using 'tingo' as provider.\n",
211
+ " >>> obb.news.world(provider='tiingo', source='bloomberg')\n",
212
+ " >>> # Filter aticles by term using 'biztoc' as provider.\n",
213
+ " >>> obb.news.world(provider='biztoc', term='apple')\n",
214
+ "\n"
215
+ ]
216
+ }
217
+ ],
218
+ "source": [
219
+ "help(obb.news.world)"
220
+ ]
221
+ },
222
+ {
223
+ "cell_type": "markdown",
224
+ "metadata": {},
225
+ "source": [
226
+ "Uniform interface allows switching between providers"
227
+ ]
228
+ },
229
+ {
230
+ "cell_type": "code",
231
+ "execution_count": 5,
232
+ "metadata": {},
233
+ "outputs": [
234
+ {
235
+ "data": {
236
+ "text/html": [
237
+ "<div>\n",
238
+ "<style scoped>\n",
239
+ " .dataframe tbody tr th:only-of-type {\n",
240
+ " vertical-align: middle;\n",
241
+ " }\n",
242
+ "\n",
243
+ " .dataframe tbody tr th {\n",
244
+ " vertical-align: top;\n",
245
+ " }\n",
246
+ "\n",
247
+ " .dataframe thead th {\n",
248
+ " text-align: right;\n",
249
+ " }\n",
250
+ "</style>\n",
251
+ "<table border=\"1\" class=\"dataframe\">\n",
252
+ " <thead>\n",
253
+ " <tr style=\"text-align: right;\">\n",
254
+ " <th>date</th>\n",
255
+ " <th>2024-08-22 18:02:00+00:00</th>\n",
256
+ " </tr>\n",
257
+ " </thead>\n",
258
+ " <tbody>\n",
259
+ " <tr>\n",
260
+ " <th>title</th>\n",
261
+ " <td>Natural Grocers® Teams Up With Local Artist, S...</td>\n",
262
+ " </tr>\n",
263
+ " <tr>\n",
264
+ " <th>text</th>\n",
265
+ " <td>Natural Grocers®, the leading family-operated ...</td>\n",
266
+ " </tr>\n",
267
+ " <tr>\n",
268
+ " <th>url</th>\n",
269
+ " <td>https://finance.yahoo.com/news/natural-grocers...</td>\n",
270
+ " </tr>\n",
271
+ " <tr>\n",
272
+ " <th>source</th>\n",
273
+ " <td>yahoo</td>\n",
274
+ " </tr>\n",
275
+ " <tr>\n",
276
+ " <th>id</th>\n",
277
+ " <td>new_DDGR2v</td>\n",
278
+ " </tr>\n",
279
+ " <tr>\n",
280
+ " <th>company</th>\n",
281
+ " <td>{'id': 'com_g4Q8NX', 'ticker': 'NGVC', 'name':...</td>\n",
282
+ " </tr>\n",
283
+ " </tbody>\n",
284
+ "</table>\n",
285
+ "</div>"
286
+ ],
287
+ "text/plain": [
288
+ "date 2024-08-22 18:02:00+00:00\n",
289
+ "title Natural Grocers® Teams Up With Local Artist, S...\n",
290
+ "text Natural Grocers®, the leading family-operated ...\n",
291
+ "url https://finance.yahoo.com/news/natural-grocers...\n",
292
+ "source yahoo\n",
293
+ "id new_DDGR2v\n",
294
+ "company {'id': 'com_g4Q8NX', 'ticker': 'NGVC', 'name':..."
295
+ ]
296
+ },
297
+ "execution_count": 5,
298
+ "metadata": {},
299
+ "output_type": "execute_result"
300
+ }
301
+ ],
302
+ "source": [
303
+ "obb.news.world(limit=1, provider=\"intrinio\").to_df().T"
304
+ ]
305
+ },
306
+ {
307
+ "cell_type": "code",
308
+ "execution_count": 6,
309
+ "metadata": {},
310
+ "outputs": [
311
+ {
312
+ "data": {
313
+ "text/html": [
314
+ "<div>\n",
315
+ "<style scoped>\n",
316
+ " .dataframe tbody tr th:only-of-type {\n",
317
+ " vertical-align: middle;\n",
318
+ " }\n",
319
+ "\n",
320
+ " .dataframe tbody tr th {\n",
321
+ " vertical-align: top;\n",
322
+ " }\n",
323
+ "\n",
324
+ " .dataframe thead th {\n",
325
+ " text-align: right;\n",
326
+ " }\n",
327
+ "</style>\n",
328
+ "<table border=\"1\" class=\"dataframe\">\n",
329
+ " <thead>\n",
330
+ " <tr style=\"text-align: right;\">\n",
331
+ " <th>date</th>\n",
332
+ " <th>2024-08-22 14:46:33-04:00</th>\n",
333
+ " </tr>\n",
334
+ " </thead>\n",
335
+ " <tbody>\n",
336
+ " <tr>\n",
337
+ " <th>title</th>\n",
338
+ " <td>Behind the Scenes of Vertiv Hldgs's Latest Opt...</td>\n",
339
+ " </tr>\n",
340
+ " <tr>\n",
341
+ " <th>images</th>\n",
342
+ " <td>[{'size': 'thumb', 'url': 'https://cdn.benzing...</td>\n",
343
+ " </tr>\n",
344
+ " <tr>\n",
345
+ " <th>text</th>\n",
346
+ " <td>&lt;p&gt;Whales with a lot of money to spend have ta...</td>\n",
347
+ " </tr>\n",
348
+ " <tr>\n",
349
+ " <th>url</th>\n",
350
+ " <td>https://www.benzinga.com/insights/options/24/0...</td>\n",
351
+ " </tr>\n",
352
+ " <tr>\n",
353
+ " <th>id</th>\n",
354
+ " <td>40515079</td>\n",
355
+ " </tr>\n",
356
+ " <tr>\n",
357
+ " <th>author</th>\n",
358
+ " <td>Benzinga Insights</td>\n",
359
+ " </tr>\n",
360
+ " <tr>\n",
361
+ " <th>teaser</th>\n",
362
+ " <td></td>\n",
363
+ " </tr>\n",
364
+ " <tr>\n",
365
+ " <th>channels</th>\n",
366
+ " <td>Options,Markets</td>\n",
367
+ " </tr>\n",
368
+ " <tr>\n",
369
+ " <th>stocks</th>\n",
370
+ " <td>VRT</td>\n",
371
+ " </tr>\n",
372
+ " <tr>\n",
373
+ " <th>tags</th>\n",
374
+ " <td>BZI-UOA</td>\n",
375
+ " </tr>\n",
376
+ " <tr>\n",
377
+ " <th>updated</th>\n",
378
+ " <td>2024-08-22 14:46:33-04:00</td>\n",
379
+ " </tr>\n",
380
+ " </tbody>\n",
381
+ "</table>\n",
382
+ "</div>"
383
+ ],
384
+ "text/plain": [
385
+ "date 2024-08-22 14:46:33-04:00\n",
386
+ "title Behind the Scenes of Vertiv Hldgs's Latest Opt...\n",
387
+ "images [{'size': 'thumb', 'url': 'https://cdn.benzing...\n",
388
+ "text <p>Whales with a lot of money to spend have ta...\n",
389
+ "url https://www.benzinga.com/insights/options/24/0...\n",
390
+ "id 40515079\n",
391
+ "author Benzinga Insights\n",
392
+ "teaser \n",
393
+ "channels Options,Markets\n",
394
+ "stocks VRT\n",
395
+ "tags BZI-UOA\n",
396
+ "updated 2024-08-22 14:46:33-04:00"
397
+ ]
398
+ },
399
+ "execution_count": 6,
400
+ "metadata": {},
401
+ "output_type": "execute_result"
402
+ }
403
+ ],
404
+ "source": [
405
+ "obb.news.world(limit=1, provider=\"benzinga\").to_df().T"
406
+ ]
407
+ },
408
+ {
409
+ "cell_type": "markdown",
410
+ "metadata": {},
411
+ "source": [
412
+ "\n",
413
+ "\n",
414
+ "\n",
415
+ "---\n",
416
+ "\n",
417
+ "\n"
418
+ ]
419
+ },
420
+ {
421
+ "cell_type": "markdown",
422
+ "metadata": {},
423
+ "source": [
424
+ "## Standardization of input and output schemas is done with Pydantic models"
425
+ ]
426
+ },
427
+ {
428
+ "cell_type": "markdown",
429
+ "metadata": {},
430
+ "source": [
431
+ "#### This is a standard model"
432
+ ]
433
+ },
434
+ {
435
+ "cell_type": "code",
436
+ "execution_count": 7,
437
+ "metadata": {},
438
+ "outputs": [
439
+ {
440
+ "data": {
441
+ "text/plain": [
442
+ "{'date': FieldInfo(annotation=datetime, required=True, alias_priority=1, validation_alias='date', serialization_alias='date', description='The date of the data. The published date of the article.'),\n",
443
+ " 'title': FieldInfo(annotation=str, required=True, alias_priority=1, validation_alias='title', serialization_alias='title', description='Title of the article.'),\n",
444
+ " 'images': FieldInfo(annotation=Union[List[Dict[str, str]], NoneType], required=False, default=None, alias_priority=1, validation_alias='images', serialization_alias='images', description='Images associated with the article.'),\n",
445
+ " 'text': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, alias_priority=1, validation_alias='text', serialization_alias='text', description='Text/body of the article.'),\n",
446
+ " 'url': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, alias_priority=1, validation_alias='url', serialization_alias='url', description='URL to the article.')}"
447
+ ]
448
+ },
449
+ "execution_count": 7,
450
+ "metadata": {},
451
+ "output_type": "execute_result"
452
+ }
453
+ ],
454
+ "source": [
455
+ "from openbb_core.provider.standard_models.world_news import WorldNewsData\n",
456
+ "\n",
457
+ "WorldNewsData.__fields__"
458
+ ]
459
+ },
460
+ {
461
+ "cell_type": "markdown",
462
+ "metadata": {},
463
+ "source": [
464
+ "#### These are provider models"
465
+ ]
466
+ },
467
+ {
468
+ "cell_type": "code",
469
+ "execution_count": 8,
470
+ "metadata": {},
471
+ "outputs": [],
472
+ "source": [
473
+ "from openbb_intrinio.models.world_news import IntrinioWorldNewsData\n",
474
+ "from openbb_benzinga.models.world_news import BenzingaWorldNewsData"
475
+ ]
476
+ },
477
+ {
478
+ "cell_type": "code",
479
+ "execution_count": 9,
480
+ "metadata": {},
481
+ "outputs": [
482
+ {
483
+ "data": {
484
+ "text/plain": [
485
+ "{'date': FieldInfo(annotation=datetime, required=True, alias_priority=1, validation_alias='date', serialization_alias='date', description='The date of the data. The published date of the article.'),\n",
486
+ " 'title': FieldInfo(annotation=str, required=True, alias_priority=1, validation_alias='title', serialization_alias='title', description='Title of the article.'),\n",
487
+ " 'images': FieldInfo(annotation=Union[List[Dict[str, str]], NoneType], required=False, default=None, alias_priority=1, validation_alias='images', serialization_alias='images', description='Images associated with the article.'),\n",
488
+ " 'text': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, alias_priority=1, validation_alias='text', serialization_alias='text', description='Text/body of the article.'),\n",
489
+ " 'url': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, alias_priority=1, validation_alias='url', serialization_alias='url', description='URL to the article.'),\n",
490
+ " 'source': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, alias_priority=1, validation_alias='source', serialization_alias='source', description='The source of the news article.'),\n",
491
+ " 'summary': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, alias_priority=1, validation_alias='summary', serialization_alias='summary', description='The summary of the news article.'),\n",
492
+ " 'topics': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, alias_priority=1, validation_alias='topics', serialization_alias='topics', description='The topics related to the news article.'),\n",
493
+ " 'word_count': FieldInfo(annotation=Union[int, NoneType], required=False, default=None, alias_priority=1, validation_alias='wordCount', serialization_alias='word_count', description='The word count of the news article.'),\n",
494
+ " 'business_relevance': FieldInfo(annotation=Union[float, NoneType], required=False, default=None, alias_priority=1, validation_alias='businessRelevance', serialization_alias='business_relevance', description=' \\tHow strongly correlated the news article is to the business'),\n",
495
+ " 'sentiment': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, alias_priority=1, validation_alias='sentiment', serialization_alias='sentiment', description='The sentiment of the news article - i.e, negative, positive.'),\n",
496
+ " 'sentiment_confidence': FieldInfo(annotation=Union[float, NoneType], required=False, default=None, alias_priority=1, validation_alias='sentimentConfidence', serialization_alias='sentiment_confidence', description='The confidence score of the sentiment rating.'),\n",
497
+ " 'language': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, alias_priority=1, validation_alias='language', serialization_alias='language', description='The language of the news article.'),\n",
498
+ " 'spam': FieldInfo(annotation=Union[bool, NoneType], required=False, default=None, alias_priority=1, validation_alias='spam', serialization_alias='spam', description='Whether the news article is spam.'),\n",
499
+ " 'copyright': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, alias_priority=1, validation_alias='copyright', serialization_alias='copyright', description='The copyright notice of the news article.'),\n",
500
+ " 'id': FieldInfo(annotation=str, required=True, alias_priority=1, validation_alias='id', serialization_alias='id', description='Article ID.'),\n",
501
+ " 'company': FieldInfo(annotation=Union[IntrinioCompany, NoneType], required=False, default=None, alias_priority=1, validation_alias='company', serialization_alias='company', description='The Intrinio Company object. Contains details company reference data.'),\n",
502
+ " 'security': FieldInfo(annotation=Union[IntrinioSecurity, NoneType], required=False, default=None, alias_priority=1, validation_alias='security', serialization_alias='security', description='The Intrinio Security object. Contains the security details related to the news article.')}"
503
+ ]
504
+ },
505
+ "execution_count": 9,
506
+ "metadata": {},
507
+ "output_type": "execute_result"
508
+ }
509
+ ],
510
+ "source": [
511
+ "IntrinioWorldNewsData.__fields__"
512
+ ]
513
+ },
514
+ {
515
+ "cell_type": "code",
516
+ "execution_count": 10,
517
+ "metadata": {},
518
+ "outputs": [
519
+ {
520
+ "data": {
521
+ "text/plain": [
522
+ "{'date': FieldInfo(annotation=datetime, required=True, alias_priority=1, validation_alias='date', serialization_alias='date', description='The date of the data. The published date of the article.'),\n",
523
+ " 'title': FieldInfo(annotation=str, required=True, alias_priority=1, validation_alias='title', serialization_alias='title', description='Title of the article.'),\n",
524
+ " 'images': FieldInfo(annotation=Union[List[Dict[str, str]], NoneType], required=False, default=None, alias_priority=1, validation_alias='images', serialization_alias='images', description='Images associated with the article.'),\n",
525
+ " 'text': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, alias_priority=1, validation_alias='text', serialization_alias='text', description='Text/body of the article.'),\n",
526
+ " 'url': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, alias_priority=1, validation_alias='url', serialization_alias='url', description='URL to the article.'),\n",
527
+ " 'id': FieldInfo(annotation=str, required=True, alias_priority=1, validation_alias='id', serialization_alias='id', description='Article ID.'),\n",
528
+ " 'author': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, alias_priority=1, validation_alias='author', serialization_alias='author', description='Author of the news.'),\n",
529
+ " 'teaser': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, alias_priority=1, validation_alias='teaser', serialization_alias='teaser', description='Teaser of the news.'),\n",
530
+ " 'channels': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, alias_priority=1, validation_alias='channels', serialization_alias='channels', description='Channels associated with the news.'),\n",
531
+ " 'stocks': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, alias_priority=1, validation_alias='stocks', serialization_alias='stocks', description='Stocks associated with the news.'),\n",
532
+ " 'tags': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, alias_priority=1, validation_alias='tags', serialization_alias='tags', description='Tags associated with the news.'),\n",
533
+ " 'updated': FieldInfo(annotation=Union[datetime, NoneType], required=False, default=None, alias_priority=1, validation_alias='updated', serialization_alias='updated', description='Updated date of the news.')}"
534
+ ]
535
+ },
536
+ "execution_count": 10,
537
+ "metadata": {},
538
+ "output_type": "execute_result"
539
+ }
540
+ ],
541
+ "source": [
542
+ "BenzingaWorldNewsData.__fields__"
543
+ ]
544
+ },
545
+ {
546
+ "cell_type": "markdown",
547
+ "metadata": {},
548
+ "source": [
549
+ "## Inheritance, field mapping and quality assurance"
550
+ ]
551
+ },
552
+ {
553
+ "cell_type": "markdown",
554
+ "metadata": {},
555
+ "source": [
556
+ "#### Provider models inherit from Standard Models"
557
+ ]
558
+ },
559
+ {
560
+ "cell_type": "code",
561
+ "execution_count": 11,
562
+ "metadata": {},
563
+ "outputs": [
564
+ {
565
+ "data": {
566
+ "text/plain": [
567
+ "True"
568
+ ]
569
+ },
570
+ "execution_count": 11,
571
+ "metadata": {},
572
+ "output_type": "execute_result"
573
+ }
574
+ ],
575
+ "source": [
576
+ "issubclass(BenzingaWorldNewsData, WorldNewsData)"
577
+ ]
578
+ },
579
+ {
580
+ "cell_type": "markdown",
581
+ "metadata": {},
582
+ "source": [
583
+ "#### Provider models use aliases to map to standard fields"
584
+ ]
585
+ },
586
+ {
587
+ "cell_type": "code",
588
+ "execution_count": 12,
589
+ "metadata": {},
590
+ "outputs": [
591
+ {
592
+ "data": {
593
+ "text/plain": [
594
+ "{'date': 'created', 'text': 'body', 'images': 'image'}"
595
+ ]
596
+ },
597
+ "execution_count": 12,
598
+ "metadata": {},
599
+ "output_type": "execute_result"
600
+ }
601
+ ],
602
+ "source": [
603
+ "BenzingaWorldNewsData.__alias_dict__"
604
+ ]
605
+ },
606
+ {
607
+ "cell_type": "markdown",
608
+ "metadata": {},
609
+ "source": [
610
+ "#### Provider models implement field validation"
611
+ ]
612
+ },
613
+ {
614
+ "cell_type": "code",
615
+ "execution_count": 13,
616
+ "metadata": {},
617
+ "outputs": [
618
+ {
619
+ "data": {
620
+ "text/plain": [
621
+ "{'date_validate': Decorator(cls_ref='openbb_intrinio.models.world_news.IntrinioWorldNewsData:140298041431344', cls_var_name='date_validate', func=<bound method IntrinioWorldNewsData.date_validate of <class 'openbb_intrinio.models.world_news.IntrinioWorldNewsData'>>, shim=None, info=FieldValidatorDecoratorInfo(fields=('publication_date',), mode='before', check_fields=False)),\n",
622
+ " 'topics_validate': Decorator(cls_ref='openbb_intrinio.models.world_news.IntrinioWorldNewsData:140298041431344', cls_var_name='topics_validate', func=<bound method IntrinioWorldNewsData.topics_validate of <class 'openbb_intrinio.models.world_news.IntrinioWorldNewsData'>>, shim=None, info=FieldValidatorDecoratorInfo(fields=('topics',), mode='before', check_fields=False)),\n",
623
+ " 'copyright_validate': Decorator(cls_ref='openbb_intrinio.models.world_news.IntrinioWorldNewsData:140298041431344', cls_var_name='copyright_validate', func=<bound method IntrinioWorldNewsData.copyright_validate of <class 'openbb_intrinio.models.world_news.IntrinioWorldNewsData'>>, shim=None, info=FieldValidatorDecoratorInfo(fields=('copyright',), mode='before', check_fields=False))}"
624
+ ]
625
+ },
626
+ "execution_count": 13,
627
+ "metadata": {},
628
+ "output_type": "execute_result"
629
+ }
630
+ ],
631
+ "source": [
632
+ "IntrinioWorldNewsData.__dict__[\"__pydantic_decorators__\"].field_validators"
633
+ ]
634
+ },
635
+ {
636
+ "cell_type": "code",
637
+ "execution_count": 14,
638
+ "metadata": {},
639
+ "outputs": [
640
+ {
641
+ "data": {
642
+ "text/plain": [
643
+ "{'date_validate': Decorator(cls_ref='openbb_benzinga.models.world_news.BenzingaWorldNewsData:140297991464784', cls_var_name='date_validate', func=<bound method BenzingaWorldNewsData.date_validate of <class 'openbb_benzinga.models.world_news.BenzingaWorldNewsData'>>, shim=None, info=FieldValidatorDecoratorInfo(fields=('date', 'updated'), mode='before', check_fields=False)),\n",
644
+ " 'list_validate': Decorator(cls_ref='openbb_benzinga.models.world_news.BenzingaWorldNewsData:140297991464784', cls_var_name='list_validate', func=<bound method BenzingaWorldNewsData.list_validate of <class 'openbb_benzinga.models.world_news.BenzingaWorldNewsData'>>, shim=None, info=FieldValidatorDecoratorInfo(fields=('stocks', 'channels', 'tags'), mode='before', check_fields=False)),\n",
645
+ " 'id_validate': Decorator(cls_ref='openbb_benzinga.models.world_news.BenzingaWorldNewsData:140297991464784', cls_var_name='id_validate', func=<bound method BenzingaWorldNewsData.id_validate of <class 'openbb_benzinga.models.world_news.BenzingaWorldNewsData'>>, shim=None, info=FieldValidatorDecoratorInfo(fields=('id', 'text', 'teaser', 'title', 'author'), mode='before', check_fields=False)),\n",
646
+ " 'empty_list': Decorator(cls_ref='openbb_benzinga.models.world_news.BenzingaWorldNewsData:140297991464784', cls_var_name='empty_list', func=<bound method BenzingaWorldNewsData.empty_list of <class 'openbb_benzinga.models.world_news.BenzingaWorldNewsData'>>, shim=None, info=FieldValidatorDecoratorInfo(fields=('images',), mode='before', check_fields=False))}"
647
+ ]
648
+ },
649
+ "execution_count": 14,
650
+ "metadata": {},
651
+ "output_type": "execute_result"
652
+ }
653
+ ],
654
+ "source": [
655
+ "BenzingaWorldNewsData.__dict__[\"__pydantic_decorators__\"].field_validators"
656
+ ]
657
+ },
658
+ {
659
+ "cell_type": "markdown",
660
+ "metadata": {},
661
+ "source": [
662
+ "Example:\n",
663
+ "\n",
664
+ "```python\n",
665
+ "@field_validator(\"date\")\n",
666
+ "def date_validate(cls, v):\n",
667
+ " \"\"\"Return the date as a datetime object.\"\"\"\n",
668
+ " return datetime.strptime(v, \"%a, %d %b %Y %H:%M:%S %z\")\n",
669
+ "```\n"
670
+ ]
671
+ },
672
+ {
673
+ "cell_type": "markdown",
674
+ "metadata": {},
675
+ "source": [
676
+ "---"
677
+ ]
678
+ },
679
+ {
680
+ "cell_type": "markdown",
681
+ "metadata": {},
682
+ "source": [
683
+ "## Modularity"
684
+ ]
685
+ },
686
+ {
687
+ "cell_type": "code",
688
+ "execution_count": null,
689
+ "metadata": {},
690
+ "outputs": [],
691
+ "source": [
692
+ "obb"
693
+ ]
694
+ },
695
+ {
696
+ "cell_type": "markdown",
697
+ "metadata": {},
698
+ "source": [
699
+ "#### Each extension and provider integration is a separate python package"
700
+ ]
701
+ },
702
+ {
703
+ "cell_type": "code",
704
+ "execution_count": null,
705
+ "metadata": {},
706
+ "outputs": [],
707
+ "source": [
708
+ "!pip list | grep openbb"
709
+ ]
710
+ },
711
+ {
712
+ "cell_type": "markdown",
713
+ "metadata": {},
714
+ "source": [
715
+ "#### Install/Uninstall a provider as python packages"
716
+ ]
717
+ },
718
+ {
719
+ "cell_type": "code",
720
+ "execution_count": null,
721
+ "metadata": {},
722
+ "outputs": [],
723
+ "source": [
724
+ "!pip uninstall openbb-yfinance"
725
+ ]
726
+ },
727
+ {
728
+ "cell_type": "markdown",
729
+ "metadata": {},
730
+ "source": [
731
+ "To learn more about how it works, here are a few links to the [documentation](https://docs.openbb.co/platform):\n",
732
+ "\n",
733
+ "- [Architecture. Data, Query Parameters and Fetchers.](https://docs.openbb.co/platform/developer_guide/architecture_overview)\n",
734
+ "- [Integrating a new provider.](https://docs.openbb.co/platform/user_guides/add_data_provider_extension)\n",
735
+ "- [Building standalone extensions.](https://docs.openbb.co/platform/getting_started/create_new_provider_extension)\n",
736
+ "- and more in the [Development](https://docs.openbb.co/platform/developer_guide) section of the docs...\n"
737
+ ]
738
+ }
739
+ ],
740
+ "metadata": {
741
+ "kernelspec": {
742
+ "display_name": "venv",
743
+ "language": "python",
744
+ "name": "python3"
745
+ },
746
+ "language_info": {
747
+ "codemirror_mode": {
748
+ "name": "ipython",
749
+ "version": 3
750
+ },
751
+ "file_extension": ".py",
752
+ "mimetype": "text/x-python",
753
+ "name": "python",
754
+ "nbconvert_exporter": "python",
755
+ "pygments_lexer": "ipython3",
756
+ "version": "3.12.4"
757
+ }
758
+ },
759
+ "nbformat": 4,
760
+ "nbformat_minor": 2
761
+ }
examples/portfolioOptimizationUsingModernPortfolioTheory.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
examples/riskReturnAnalysis.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
examples/sectorRotationStrategy.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
examples/streamlit/news.py ADDED
@@ -0,0 +1,444 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Streamlit News Page"""
2
+
3
+ # flake8: noqa: I001
4
+
5
+ from datetime import datetime, timedelta
6
+
7
+ from openbb import obb
8
+ from numpy import nan
9
+
10
+ import streamlit as st
11
+
12
+ st.set_page_config(
13
+ layout="wide",
14
+ page_title="News",
15
+ initial_sidebar_state="expanded",
16
+ )
17
+
18
+ st.sidebar.markdown(
19
+ """
20
+ <style>
21
+ section[data-testid="stSidebar"] {
22
+ top: 1% !important;
23
+ height: 98.25% !important;
24
+ left: 0.33% !important;
25
+ margin-top: 0 !important;
26
+ }
27
+ section[data-testid="stSidebar"] img {
28
+ margin-top: -75px !important;
29
+ margin-left: -10px !important;
30
+ width: 95% !important;
31
+ }
32
+ section[data-testid="stVerticalBlock"] {
33
+ gap: 0rem;
34
+ }
35
+ body {
36
+ line-height: 1.2;
37
+ }
38
+ </style>
39
+ <figure style='text-align: center;'>
40
+ <img src='https://openbb.co/assets/images/ogimages/Homepage.png' />
41
+ <figcaption style='font-size: 0.8em; color: #888;'>Powered by Open Source</figcaption>
42
+ </figure>
43
+ """,
44
+ unsafe_allow_html=True,
45
+ )
46
+
47
+
48
+ button_pressed = False
49
+
50
+ SUPPORTED_SOURCES = ["benzinga", "biztoc", "intrinio", "fmp", "tiingo"]
51
+
52
+ providers = [
53
+ d
54
+ for d in list(obb.user.credentials.__dict__.keys()) # type: ignore
55
+ if obb.user.credentials.__dict__[d] is not None # type: ignore
56
+ ]
57
+ providers = [d.split("_")[0] for d in providers if d.split("_")[0] in SUPPORTED_SOURCES]
58
+ news_sources = [d.upper() if d == "fmp" else d.title() for d in providers]
59
+
60
+ if "news" not in st.session_state:
61
+ st.session_state.news = None
62
+
63
+ if "biztoc_sources" not in st.session_state:
64
+ st.session_state.biztoc_sources = []
65
+ if "news_container" not in st.session_state:
66
+ st.session_state.news_container = st.empty()
67
+ if "selected_limit" not in st.session_state:
68
+ st.session_state.selected_limit = 100
69
+ if "selected_provider" not in st.session_state:
70
+ if len(news_sources) == 0:
71
+ st.error(
72
+ f"No news sources available. Please check your credentials for one of: {SUPPORTED_SOURCES}"
73
+ )
74
+ st.stop()
75
+ if len(news_sources) > 0:
76
+ st.session_state.selected_provider = (
77
+ "Biztoc" if "Biztoc" in news_sources else news_sources[0]
78
+ )
79
+ if "selected_tags" not in st.session_state:
80
+ st.session_state.selected_tags = ""
81
+ if "selected_term" not in st.session_state:
82
+ st.session_state.selected_term = ""
83
+ if "news_start_date" not in st.session_state:
84
+ st.session_state.news_start_date = (datetime.now() - timedelta(days=2)).date()
85
+ if "news_end_date" not in st.session_state:
86
+ st.session_state.news_end_date = datetime.now().date()
87
+ if "selected_biztoc_source" not in st.session_state:
88
+ st.session_state.selected_biztoc_source = ""
89
+ if "content_type" not in st.session_state:
90
+ st.session_state.content_type = "news"
91
+ if "benzinga_tickers" not in st.session_state:
92
+ st.session_state.benzinga_tickers = ""
93
+ if "selected_benzinga_channel" not in st.session_state:
94
+ st.session_state.selected_benzinga_channel = ""
95
+ if "fmp_tickers" not in st.session_state:
96
+ st.session_state.fmp_tickers = ""
97
+ if "intrinio_tickers" not in st.session_state:
98
+ st.session_state.intrinio_tickers = ""
99
+ if "tiingo_tickers" not in st.session_state:
100
+ st.session_state.tiingo_tickers = ""
101
+ if "tiingo_source" not in st.session_state:
102
+ st.session_state.tiingo_source = ""
103
+
104
+
105
+ def fetch_openbb():
106
+ kwargs = {
107
+ "provider": st.session_state.selected_provider.lower(),
108
+ "limit": st.session_state.selected_limit,
109
+ }
110
+ if st.session_state.selected_provider == "Benzinga":
111
+ kwargs["start_date"] = st.session_state.news_start_date.strftime("%Y-%m-%d")
112
+ kwargs["end_date"] = st.session_state.news_end_date.strftime("%Y-%m-%d")
113
+ kwargs["topics"] = st.session_state.selected_tags
114
+ kwargs["display"] = "full"
115
+ kwargs["page_size"] = 100
116
+ kwargs["channels"] = (
117
+ st.session_state.selected_benzinga_channel.lower()
118
+ if st.session_state.selected_benzinga_channel
119
+ else None
120
+ )
121
+ kwargs["symbol"] = (
122
+ st.session_state.benzinga_tickers
123
+ if st.session_state.benzinga_tickers
124
+ else None
125
+ )
126
+
127
+ if st.session_state.selected_provider == "Biztoc":
128
+ kwargs["term"] = st.session_state.selected_term
129
+ kwargs["tag"] = st.session_state.selected_tags
130
+ kwargs["filter"] = "tag" if kwargs.get("tag") else None
131
+ if kwargs.get("filter") is None:
132
+ kwargs["filter"] = "latest"
133
+ kwargs["source"] = (
134
+ st.session_state.selected_biztoc_source
135
+ if st.session_state.selected_biztoc_source
136
+ else None
137
+ )
138
+ kwargs["filter"] = "source" if kwargs.get("source") else kwargs.get("filter")
139
+ if kwargs.get("filter") == "source":
140
+ kwargs.pop("tag")
141
+
142
+ if st.session_state.selected_provider == "FMP":
143
+ kwargs["symbol"] = (
144
+ st.session_state.fmp_tickers if st.session_state.fmp_tickers else None
145
+ )
146
+
147
+ if st.session_state.selected_provider == "Intrinio":
148
+ kwargs["symbol"] = (
149
+ st.session_state.intrinio_tickers
150
+ if st.session_state.intrinio_tickers
151
+ else None
152
+ )
153
+
154
+ if st.session_state.selected_provider == "Tiingo":
155
+ kwargs["start_date"] = st.session_state.news_start_date.strftime("%Y-%m-%d")
156
+ kwargs["end_date"] = st.session_state.news_end_date.strftime("%Y-%m-%d")
157
+ kwargs["symbol"] = (
158
+ st.session_state.tiingo_tickers if st.session_state.tiingo_tickers else None
159
+ )
160
+
161
+ kwargs = {key: value for key, value in kwargs.items() if value is not None}
162
+
163
+ data = (
164
+ obb.news.company(**kwargs) # type: ignore
165
+ if kwargs.get("symbol")
166
+ else obb.news.world(**kwargs) # type: ignore
167
+ )
168
+ if data.results != []:
169
+ return data.to_df().sort_index(ascending=False).reset_index()
170
+
171
+
172
+ def update_data():
173
+ st.session_state.news = fetch_openbb()
174
+
175
+
176
+ with st.sidebar:
177
+ c1, c2 = st.columns(2)
178
+ with c1:
179
+ old_start_date = st.session_state.news_start_date
180
+ old_provider = st.session_state.selected_provider
181
+ st.session_state.selected_provider = st.selectbox(
182
+ label="Provider",
183
+ options=news_sources,
184
+ index=news_sources.index(st.session_state.selected_provider),
185
+ )
186
+ old_tags = st.session_state.selected_tags
187
+ if st.session_state.selected_provider == "Benzinga":
188
+ st.session_state.news_start_date = st.date_input(
189
+ "Start Date", value=old_start_date
190
+ )
191
+ st.session_state.selected_tags = st.text_input(
192
+ label="Tag", value=st.session_state.selected_tags
193
+ )
194
+ old_biztoc_source = st.session_state.selected_biztoc_source
195
+ if st.session_state.selected_provider == "Biztoc":
196
+
197
+ st.session_state.selected_biztoc_source = st.text_input(label="Source")
198
+ old_benzinga_tickers = st.session_state.benzinga_tickers
199
+ if st.session_state.selected_provider == "Benzinga":
200
+ st.session_state.benzinga_tickers = st.text_input(
201
+ label="Tickers", value=old_benzinga_tickers
202
+ )
203
+ old_fmp_tickers = st.session_state.fmp_tickers
204
+ if st.session_state.selected_provider == "FMP":
205
+ st.session_state.fmp_tickers = st.text_input(
206
+ label="Tickers", value=old_fmp_tickers
207
+ )
208
+ old_intrinio_tickers = st.session_state.intrinio_tickers
209
+ if st.session_state.selected_provider == "Intrinio":
210
+ st.session_state.intrinio_tickers = st.text_input(
211
+ label="Tickers", value=old_intrinio_tickers
212
+ )
213
+ old_tiingo_tickers = st.session_state.tiingo_tickers
214
+ if st.session_state.selected_provider == "Tiingo":
215
+ st.session_state.news_start_date = st.date_input(
216
+ "Start Date", value=old_start_date
217
+ )
218
+ old_tiingo_tickers = st.session_state.tiingo_tickers
219
+ st.session_state.tiingo_tickers = st.text_input(
220
+ label="Tickers", value=st.session_state.tiingo_tickers
221
+ )
222
+ with c2:
223
+ old_limit = st.session_state.selected_limit
224
+ old_end_date = st.session_state.news_end_date
225
+ st.session_state.selected_limit = st.number_input(
226
+ "Number of Stories", min_value=1, value=100
227
+ )
228
+ old_channel = st.session_state.selected_benzinga_channel
229
+ if st.session_state.selected_provider == "Benzinga":
230
+ st.session_state.news_end_date = st.date_input(
231
+ "End Date", value=old_end_date
232
+ )
233
+ st.session_state.selected_benzinga_channel = st.text_input(
234
+ label="Feed Channel", value=old_channel
235
+ )
236
+ old_term = st.session_state.selected_term
237
+ if st.session_state.selected_provider == "Biztoc":
238
+ st.session_state.selected_term = st.text_input(
239
+ label="Search Term", value=old_term
240
+ )
241
+ if st.session_state.selected_provider == "Tiingo":
242
+ st.session_state.news_end_date = st.date_input(
243
+ "End Date", value=old_end_date
244
+ )
245
+
246
+ if any(
247
+ [
248
+ old_start_date != st.session_state.news_start_date,
249
+ old_end_date != st.session_state.news_end_date,
250
+ old_limit != st.session_state.selected_limit,
251
+ old_provider != st.session_state.selected_provider,
252
+ old_tags != st.session_state.selected_tags,
253
+ old_term != st.session_state.selected_term,
254
+ old_biztoc_source != st.session_state.selected_biztoc_source,
255
+ old_channel != st.session_state.selected_benzinga_channel,
256
+ old_benzinga_tickers != st.session_state.benzinga_tickers,
257
+ old_fmp_tickers != st.session_state.fmp_tickers,
258
+ old_intrinio_tickers != st.session_state.intrinio_tickers,
259
+ old_tiingo_tickers != st.session_state.tiingo_tickers,
260
+ ]
261
+ ):
262
+ update_data()
263
+
264
+ if st.button("Fetch Data"):
265
+ update_data()
266
+
267
+
268
+ def main():
269
+ with st.session_state.news_container.container():
270
+ st.markdown(
271
+ " <style> div[class^='block-container'] { padding-top: 1rem; } h1 { margin-bottom: -10px; } </style> "
272
+ "<h1 style='text-align: center;'>Headlines and Stories</h1> ",
273
+ unsafe_allow_html=True,
274
+ )
275
+ if st.session_state.news is not None:
276
+ story = -1
277
+ expanded = False
278
+ for i in st.session_state.news.index:
279
+ story += 1
280
+ expanded = story == 0
281
+ text = (
282
+ st.session_state.news.loc[i].text
283
+ if "text" in st.session_state.news.loc[i]
284
+ else st.session_state.news.loc[i].get("title")
285
+ )
286
+ src = st.session_state.news.loc[i].url
287
+ date = str(st.session_state.news.loc[i].date)
288
+ title = st.session_state.news.loc[i].title
289
+ if text and text is not nan and text != "":
290
+ with st.expander(label=f"{date} - {title}", expanded=expanded):
291
+ st.markdown(
292
+ f"""
293
+ <div style='max-width: 90%; margin: auto; word-wrap: break-word;'>
294
+ <h2 style='text-align: center;'>{title}</h2>
295
+ </div>
296
+ """,
297
+ unsafe_allow_html=True,
298
+ )
299
+
300
+ if st.session_state.selected_provider == "Benzinga":
301
+ _tags = (
302
+ st.session_state.news.loc[i].tags
303
+ if st.session_state.news.loc[i].get("tags")
304
+ else ""
305
+ )
306
+ _stocks = (
307
+ st.session_state.news.loc[i].stocks
308
+ if st.session_state.news.loc[i].get("stocks")
309
+ else ""
310
+ )
311
+ _channels = (
312
+ st.session_state.news.loc[i].channels
313
+ if st.session_state.news.loc[i].get("channels")
314
+ else ""
315
+ )
316
+ _images = (
317
+ st.session_state.news.loc[i].images
318
+ if st.session_state.news.loc[i].get("images")
319
+ else []
320
+ )
321
+ _url = st.session_state.news.loc[i].url
322
+ st.markdown(
323
+ """
324
+ <style>
325
+ img {
326
+ max-width: 98%;
327
+ height: auto;
328
+ margin: auto;
329
+ }
330
+ </style>
331
+ """,
332
+ unsafe_allow_html=True,
333
+ )
334
+ if _images and _images is not nan:
335
+ img = _images[0].get("url")
336
+ if img is not None:
337
+ st.markdown(
338
+ f"<div style='text-align: center;'><img src='{img}'></div><br></br>",
339
+ unsafe_allow_html=True,
340
+ )
341
+ if text is not None:
342
+ st.markdown(text, unsafe_allow_html=True)
343
+ st.divider()
344
+ st.write(_url)
345
+ if _tags:
346
+ st.markdown(
347
+ f"##### Tags for this story: \n {_tags} \n"
348
+ )
349
+ if _stocks and _stocks is not nan:
350
+ st.markdown(f"##### Stocks mentioned:\n {_stocks} \n")
351
+ if _channels:
352
+ st.markdown(
353
+ f"##### Channels for this story: \n {_channels} \n"
354
+ )
355
+
356
+ if st.session_state.selected_provider == "Biztoc":
357
+ if st.session_state.news.loc[i].get("images") not in [
358
+ None,
359
+ nan,
360
+ ]:
361
+ img = st.session_state.news.loc[i].images[0].get("s")
362
+ img = (
363
+ st.session_state.news.loc[i].images.get("o")
364
+ if img is None
365
+ else img
366
+ )
367
+ if img is not None:
368
+ st.markdown(
369
+ f"<div style='text-align: center;'><img src='{img}'></div><br></br>",
370
+ unsafe_allow_html=True,
371
+ )
372
+ if text:
373
+ st.markdown(text, unsafe_allow_html=True)
374
+ st.write(src)
375
+ _story_tags = st.session_state.news.loc[i].get("tags")
376
+ _story_tags = ",".join(_story_tags) if _story_tags else ""
377
+ if _story_tags:
378
+ st.divider()
379
+ st.markdown(
380
+ f"##### Tags for this story: \n {_story_tags} \n\n"
381
+ )
382
+
383
+ if st.session_state.selected_provider == "Intrinio":
384
+ _tags = st.session_state.news.loc[i].get("tags")
385
+ _stocks = (
386
+ st.session_state.news.loc[i]["company"].get("ticker")
387
+ if st.session_state.news.loc[i].get("company")
388
+ else None
389
+ )
390
+ _images = st.session_state.news.loc[i].get("images")
391
+ _url = st.session_state.news.loc[i].get("url")
392
+ st.markdown(text, unsafe_allow_html=True)
393
+ if _url:
394
+ st.write(_url)
395
+ if _stocks and _stocks is not nan:
396
+ st.divider()
397
+ st.markdown(f"##### Stocks mentioned:\n {_stocks} \n")
398
+
399
+ if st.session_state.selected_provider == "FMP":
400
+ _url = st.session_state.news.loc[i].get("url")
401
+ _images = st.session_state.news.loc[i].get("images")
402
+ _symbols = st.session_state.news.loc[i].get("symbols")
403
+ img = (
404
+ _images[0].get("o") or _images[0].get("url")
405
+ if _images
406
+ else None
407
+ )
408
+ if img is not None:
409
+ st.markdown(
410
+ f"""
411
+ <div style='text-align: center;'>
412
+ <img src='{img}' width='95%' />
413
+ </div>
414
+ <br>
415
+ """,
416
+ unsafe_allow_html=True,
417
+ )
418
+ if text:
419
+ st.markdown(text, unsafe_allow_html=True)
420
+ if _url:
421
+ st.write(_url)
422
+
423
+ if st.session_state.selected_provider == "Tiingo":
424
+ _url = st.session_state.news.loc[i].get("url")
425
+ _tags = st.session_state.news.loc[i].get("tags")
426
+ _stocks = st.session_state.news.loc[i].get("symbols")
427
+ if _url:
428
+ st.write(_url)
429
+ st.divider()
430
+ if _tags:
431
+ st.markdown(
432
+ f"##### Tags for this story: \n {_tags} \n"
433
+ )
434
+ if _stocks and _stocks is not nan:
435
+ st.markdown(f"##### Stocks mentioned:\n {_stocks} \n")
436
+
437
+ st.divider()
438
+ st.write(
439
+ "Learn more about the OpenBB Platform [here](https://docs.openbb.co/platform)"
440
+ )
441
+
442
+
443
+ if __name__ == "__main__":
444
+ main()
examples/streamlit/requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ streamlit
2
+ openbb
3
+ openbb-biztoc
examples/streamlit_news.webp ADDED
examples/usdLiquidityIndex.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
frontend-components/fonts/FiraCode-Regular.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5992ab9640e2df491b2f609467b1de60e8bc39b2c28db184342a0592d98f6117
3
+ size 289624
frontend-components/fonts/FiraCode-VF.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:04149681350f161aca538858b88d17443c586cacca83c165939a13423fe91a26
3
+ size 286320
frontend-components/plotly/.gitignore ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Logs
2
+ logs
3
+ *.log
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+ pnpm-debug.log*
8
+ lerna-debug.log*
9
+
10
+ node_modules
11
+ dist
12
+ dist-ssr
13
+ *.local
14
+
15
+ # Editor directories and files
16
+ .vscode/*
17
+ !.vscode/extensions.json
18
+ .idea
19
+ .DS_Store
20
+ *.suo
21
+ *.ntvs*
22
+ *.njsproj
23
+ *.sln
24
+ *.sw?
frontend-components/plotly/README.md ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Getting Started with Create React App
2
+
3
+ This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4
+
5
+ ## Available Scripts
6
+
7
+ In the project directory, you can run:
8
+
9
+ ### `npm start`
10
+
11
+ Runs the app in the development mode.\
12
+ Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
13
+
14
+ The page will reload when you make changes.\
15
+ You may also see any lint errors in the console.
16
+
17
+ ### `npm test`
18
+
19
+ Launches the test runner in the interactive watch mode.\
20
+ See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21
+
22
+ ### `npm run build`
23
+
24
+ Builds the app for production to the `build` folder.\
25
+ It correctly bundles React in production mode and optimizes the build for the best performance.
26
+
27
+ The build is minified and the filenames include the hashes.\
28
+ Your app is ready to be deployed!
29
+
30
+ See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31
+
32
+ ### `npm run eject`
33
+
34
+ **Note: this is a one-way operation. Once you `eject`, you can't go back!**
35
+
36
+ If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37
+
38
+ Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
39
+
40
+ You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
41
+
42
+ ## Learn More
43
+
44
+ You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45
+
46
+ To learn React, check out the [React documentation](https://reactjs.org/).
47
+
48
+ ### Code Splitting
49
+
50
+ This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
51
+
52
+ ### Analyzing the Bundle Size
53
+
54
+ This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
55
+
56
+ ### Making a Progressive Web App
57
+
58
+ This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
59
+
60
+ ### Advanced Configuration
61
+
62
+ This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
63
+
64
+ ### Deployment
65
+
66
+ This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
67
+
68
+ ### `npm run build` fails to minify
69
+
70
+ This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
frontend-components/plotly/index.html ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <meta name="color-scheme" content="dark light" />
7
+ <title>OpenBB Interactive Charts</title>
8
+ <script>
9
+ if (
10
+ // check if user had saved dark as their
11
+ // theme when accessing page before
12
+ localStorage.theme === "dark" ||
13
+ // or user's requesting dark color
14
+ // scheme through operating system
15
+ (!("theme" in localStorage) &&
16
+ window.matchMedia("(prefers-color-scheme: dark)").matches)
17
+ ) {
18
+ // then if we have access to the document and the element
19
+ // we add the dark class to the html element and
20
+ // store the dark value in the localStorage
21
+ if (document && document.documentElement) {
22
+ document.documentElement.classList.add("dark");
23
+ localStorage.setItem("theme", "dark");
24
+ }
25
+ } else {
26
+ // else if we have access to the document and the element
27
+ // we remove the dark class to the html element and
28
+ // store the value light in the localStorage
29
+ if (document && document.documentElement) {
30
+ document.documentElement.classList.remove("dark");
31
+ localStorage.setItem("theme", "light");
32
+ }
33
+ }
34
+ </script>
35
+ </head>
36
+ <body>
37
+ <div id="root" class="h-full"></div>
38
+ <script type="module" src="/src/main.tsx"></script>
39
+ </body>
40
+ </html>
frontend-components/plotly/package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
frontend-components/plotly/package.json ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "plotly",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "vite",
7
+ "build": "vite build",
8
+ "build_tsc": "tsc && vite build",
9
+ "deploy": "npm run build && mv dist/index.html ../../openbb_platform/obbject_extensions/charting/openbb_charting/core/plotly.html",
10
+ "preview": "vite preview"
11
+ },
12
+ "dependencies": {
13
+ "@radix-ui/react-dialog": "^1.0.3",
14
+ "dom-to-image": "^2.6.0",
15
+ "esbuild": ">=0.25.0",
16
+ "lodash": "^4.17.21",
17
+ "plotly.js-dist-min": "^3.0.0",
18
+ "react": "^18.0.0",
19
+ "react-dom": "^18.0.0",
20
+ "react-plotly.js": "^2.6.0",
21
+ "rollup": ">=4.22.4"
22
+ },
23
+ "devDependencies": {
24
+ "@types/dom-to-image": "^2.6.4",
25
+ "@types/lodash": "^4.14.195",
26
+ "@types/node": "^18.16.3",
27
+ "@types/plotly.js-dist-min": "^2.3.4",
28
+ "@types/react": "^18.0.27",
29
+ "@types/react-dom": "^18.0.10",
30
+ "@types/react-plotly.js": "^2.6.3",
31
+ "@types/wicg-file-system-access": "^2020.9.6",
32
+ "@vitejs/plugin-react": "^4.2.1",
33
+ "autoprefixer": "^10.4.13",
34
+ "clsx": "^1.2.1",
35
+ "postcss": "^8.4.21",
36
+ "react-hotkeys-hook": "^4.4.0",
37
+ "tailwindcss": "^3.2.7",
38
+ "typescript": "^4.9.3",
39
+ "vite": ">=6.2.7",
40
+ "vite-plugin-singlefile": "^0.13.3"
41
+ }
42
+ }
frontend-components/plotly/postcss.config.cjs ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ module.exports = {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ autoprefixer: {},
5
+ },
6
+ };
frontend-components/plotly/src/App.tsx ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ //@ts-nocheck
2
+ import { useEffect, useState } from "react";
3
+ import Chart from "./components/Chart";
4
+ import { candlestickMockup } from "./data/mockup";
5
+
6
+ declare global {
7
+ [Exposed === Window, SecureContext];
8
+ interface Window {
9
+ json_data: any;
10
+ export_image: string;
11
+ save_image: boolean;
12
+ title: string;
13
+ Plotly: any;
14
+ MODEBAR: HTMLElement;
15
+ download_path: string;
16
+ pywry: any;
17
+ }
18
+ }
19
+
20
+ function App() {
21
+ const [json_data, setData] = useState(
22
+ process.env.NODE_ENV === "production" ? null : candlestickMockup,
23
+ );
24
+ const [options, setOptions] = useState({});
25
+
26
+ useEffect(() => {
27
+ if (process.env.NODE_ENV === "production") {
28
+ const interval = setInterval(() => {
29
+ if (window.json_data) {
30
+ const plotly_json = window.json_data;
31
+ console.log(plotly_json);
32
+ setData(plotly_json);
33
+ clearInterval(interval);
34
+ }
35
+ }, 100);
36
+ return () => clearInterval(interval);
37
+ }
38
+ }, []);
39
+
40
+ const transformData = (data: any) => {
41
+ if (!data) return null;
42
+ const globals = {
43
+ added_traces: [],
44
+ csv_yaxis_id: null,
45
+ cmd_src_idx: null,
46
+ cmd_idx: null,
47
+ cmd_src: "",
48
+ old_margin: null,
49
+ title: "",
50
+ };
51
+ const filename = data.layout?.title?.text
52
+ .replace(/ -/g, "")
53
+ .replace(/-/g, "")
54
+ .replace(/<b>|<\/b>/g, "")
55
+ .replace(/ /g, "_");
56
+ const date = new Date().toISOString().slice(0, 10).replace(/-/g, "");
57
+ const time = new Date().toISOString().slice(11, 19).replace(/:/g, "");
58
+ window.title = `openbb_${filename}_${date}_${time}`.replace(/_{2,}/g, "_");
59
+
60
+ if (data.layout.annotations !== undefined) {
61
+ data.layout.annotations.forEach(function (annotation) {
62
+ if (annotation.text !== undefined)
63
+ if (annotation.text[0] === "/") {
64
+ globals.cmd_src = annotation.text;
65
+ globals.cmd_idx = data.layout.annotations.indexOf(annotation);
66
+ annotation.text = "";
67
+
68
+ const margin = data.layout.margin;
69
+ globals.old_margin = { ...margin };
70
+ if (margin.t !== undefined && margin.t > 40) margin.t = 40;
71
+
72
+ if (data.cmd === "/equity/price/historical") margin.r -= 50;
73
+ }
74
+ });
75
+ }
76
+
77
+ // We add spaces to all trace names, due to Fira Code font width issues
78
+ // to make sure that the legend is not cut off
79
+ data.data.forEach(function (trace) {
80
+ if (trace.name !== undefined) {
81
+ trace.hoverlabel = {
82
+ namelength: -1,
83
+ };
84
+ }
85
+ });
86
+
87
+ const title = data.layout?.title?.text || "OpenBB Platform";
88
+ globals.title = title;
89
+ return {
90
+ data: data,
91
+ date: new Date(),
92
+ globals: globals,
93
+ cmd: data.command_location,
94
+ python_version: data.python_version,
95
+ pywry_version: data.pywry_version,
96
+ terminal_version: data.terminal_version,
97
+ theme: data.theme,
98
+ title,
99
+ };
100
+ };
101
+
102
+ const transformedData = transformData(json_data);
103
+
104
+ if (transformedData) {
105
+ return (
106
+ <Chart
107
+ json={transformedData.data}
108
+ date={transformedData.date}
109
+ cmd={transformedData.cmd}
110
+ title={transformedData.title}
111
+ globals={transformedData.globals}
112
+ theme={transformedData.theme}
113
+ />
114
+ );
115
+ } else
116
+ return (
117
+ <div className="absolute inset-0 flex items-center justify-center z-[100]">
118
+ <svg
119
+ className="animate-spin h-20 w-20 text-white"
120
+ xmlns="http://www.w3.org/2000/svg"
121
+ fill="none"
122
+ viewBox="0 0 24 24"
123
+ >
124
+ <circle
125
+ className="opacity-25"
126
+ cx="12"
127
+ cy="12"
128
+ r="10"
129
+ stroke="currentColor"
130
+ strokeWidth="4"
131
+ />
132
+ <path
133
+ className="opacity-75"
134
+ fill="currentColor"
135
+ d="M4 12a8 8 0 018-8v8z"
136
+ />
137
+ </svg>
138
+ </div>
139
+ );
140
+ }
141
+
142
+ export default App;
frontend-components/plotly/src/components/AutoScaling.tsx ADDED
@@ -0,0 +1,232 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ //@ts-nocheck
2
+ import { Figure } from "react-plotly.js";
3
+
4
+ export const isoDateRegex = new RegExp(
5
+ "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}",
6
+ );
7
+
8
+ function merge(target, source) {
9
+ Object.keys(source).forEach((key) => {
10
+ if (typeof source[key] === "object") {
11
+ Object.assign(source[key], merge(target[key], source[key]));
12
+ }
13
+ });
14
+ Object.assign(target || {}, source);
15
+ return target;
16
+ }
17
+
18
+ export default async function autoScaling(
19
+ eventdata: Readonly<Plotly.PlotRelayoutEvent>,
20
+ graphs: Figure,
21
+ ) {
22
+ try {
23
+ if (eventdata["xaxis.range[0]"] !== undefined) {
24
+ const x_min = eventdata["xaxis.range[0]"];
25
+ const x_max = eventdata["xaxis.range[1]"];
26
+ let x0_min = x_min;
27
+ let x1_max = x_max;
28
+
29
+ if (isoDateRegex.test(x_min.replace(" ", "T").split(".")[0])) {
30
+ x0_min = new Date(x_min.replace(" ", "T").split(".")[0]);
31
+ x1_max = new Date(x_max.replace(" ", "T").split(".")[0]);
32
+ }
33
+
34
+ const to_update = {};
35
+ const yaxis_fixedrange = [];
36
+ let y_min: number;
37
+ let y_max: number;
38
+ let min_xrange: any;
39
+
40
+ const get_all_yaxis_traces = {};
41
+ const get_all_yaxis_annotations = {};
42
+ let volumeTraceYaxis = null;
43
+
44
+ const yaxis_unique = [
45
+ ...new Set(
46
+ graphs.data.map((trace: Plotly.PlotData) => {
47
+ if (trace.y !== undefined || trace.type === "candlestick") {
48
+ if (
49
+ trace.yaxis === undefined &&
50
+ trace?.name?.trim() !== "Volume"
51
+ ) {
52
+ trace.yaxis = "y";
53
+ }
54
+ if (trace.type === "bar" && trace?.name?.trim() === "Volume") {
55
+ volumeTraceYaxis = `yaxis${trace.yaxis.replace("y", "")}`;
56
+ }
57
+ get_all_yaxis_traces[trace.yaxis] =
58
+ get_all_yaxis_traces[trace.yaxis] || [];
59
+ get_all_yaxis_traces[trace.yaxis].push(trace);
60
+ return trace.yaxis;
61
+ }
62
+ }),
63
+ ),
64
+ ];
65
+
66
+ graphs.layout.annotations.map((annotation: any, i: number) => {
67
+ if (annotation.yref !== undefined && annotation.yref !== "paper") {
68
+ annotation.index = i;
69
+ const yaxis = `yaxis${annotation.yref.replace("y", "")}`;
70
+ get_all_yaxis_annotations[yaxis] =
71
+ get_all_yaxis_annotations[yaxis] || [];
72
+ get_all_yaxis_annotations[yaxis].push(annotation);
73
+ }
74
+ });
75
+
76
+ yaxis_unique.map((unique) => {
77
+ if (typeof unique !== "string") {
78
+ return;
79
+ }
80
+ const yaxis = `yaxis${unique.replace("y", "")}`;
81
+ let y_candle = [];
82
+ let y_values = [];
83
+ let log_scale = graphs.layout[yaxis].type === "log";
84
+
85
+ get_all_yaxis_traces[unique].map((trace2) => {
86
+ const x = trace2.x;
87
+ log_scale = graphs.layout[yaxis].type === "log";
88
+
89
+ let y = trace2.y !== undefined ? trace2.y : [];
90
+ let y_low = trace2.type === "candlestick" ? trace2.low : [];
91
+ let y_high = trace2.type === "candlestick" ? trace2.high : [];
92
+
93
+ if (log_scale) {
94
+ y = y.map(Math.log10);
95
+ if (trace2.type === "candlestick") {
96
+ y_low = trace2.low.map(Math.log10);
97
+ y_high = trace2.high.map(Math.log10);
98
+ }
99
+ }
100
+
101
+ const yx_values = x.map(
102
+ (x: string | number | Date, i: string | number) => {
103
+ let out = null;
104
+
105
+ if (isoDateRegex.test(x.toString())) {
106
+ const x_time = new Date(x).getTime();
107
+ if (x_time >= x0_min.getTime() && x_time <= x1_max.getTime()) {
108
+ if (trace2.y !== undefined && y[i] !== undefined) {
109
+ out = y[i];
110
+ }
111
+ if (trace2.type === "candlestick") {
112
+ y_candle.push(y_low[i]);
113
+ y_candle.push(y_high[i]);
114
+ }
115
+ if (!min_xrange || x_time < min_xrange) {
116
+ min_xrange = x_time;
117
+ }
118
+ }
119
+ } else if (x >= x_min && x <= x_max) {
120
+ if (trace2.y !== undefined) {
121
+ out = y[i];
122
+ }
123
+ if (trace2.type === "candlestick") {
124
+ y_candle.push(y_low[i]);
125
+ y_candle.push(y_high[i]);
126
+ }
127
+ if (!min_xrange || x < min_xrange) {
128
+ min_xrange = x;
129
+ }
130
+ }
131
+ return out;
132
+ },
133
+ );
134
+
135
+ y_values = y_values.concat(yx_values);
136
+ });
137
+
138
+ y_values = y_values
139
+ .flat()
140
+ .filter((y2) => y2 !== undefined && y2 !== null);
141
+ y_min = Math.min(...y_values);
142
+ y_max = Math.max(...y_values);
143
+
144
+ if (y_candle.length > 0) {
145
+ y_candle = y_candle
146
+ .flat()
147
+ .filter((y2) => y2 !== undefined && y2 !== null);
148
+ y_min = Math.min(...y_candle);
149
+ y_max = Math.max(...y_candle);
150
+ }
151
+
152
+ const org_y_max = y_max;
153
+
154
+ if (y_min !== undefined && y_max !== undefined) {
155
+ const y_range = y_max - y_min;
156
+ let y_mult = 0.15;
157
+ if (y_candle.length > 0) {
158
+ y_mult = 0.3;
159
+ }
160
+
161
+ y_min -= y_range * y_mult;
162
+ y_max += y_range * y_mult;
163
+ if (to_update[yaxis] === undefined) {
164
+ to_update[yaxis] = {};
165
+ }
166
+
167
+ if (yaxis === volumeTraceYaxis) {
168
+ if (graphs.layout[yaxis].tickvals !== undefined) {
169
+ const range_x = 7;
170
+ const volume_ticks = org_y_max;
171
+ let round_digits = -3;
172
+ // @ts-ignore
173
+ let first_val = Math.round(volume_ticks * 0.2, round_digits);
174
+ const x_zipped = [2, 5, 6, 7, 8, 9, 10];
175
+ const y_zipped = [1, 4, 5, 6, 7, 8, 9];
176
+
177
+ for (let i = 0; i < x_zipped.length; i++) {
178
+ if (String(volume_ticks).length > x_zipped[i]) {
179
+ round_digits = -y_zipped[i];
180
+ // @ts-ignore
181
+ first_val = Math.round(volume_ticks * 0.2, round_digits);
182
+ }
183
+ }
184
+ const tickvals = [
185
+ Math.floor(first_val),
186
+ Math.floor(first_val * 2),
187
+ Math.floor(first_val * 3),
188
+ Math.floor(first_val * 4),
189
+ ];
190
+ const volume_range = [0, Math.floor(volume_ticks * range_x)];
191
+
192
+ to_update[yaxis].tickvals = tickvals;
193
+ to_update[yaxis].range = volume_range;
194
+ to_update[yaxis].tickformat = ".2s";
195
+ return;
196
+ }
197
+ y_min = 0;
198
+ y_max = graphs.layout[yaxis].range[1];
199
+ }
200
+ to_update[yaxis].range = [y_min, y_max];
201
+ to_update[yaxis].fixedrange = true;
202
+ yaxis_fixedrange.push(yaxis);
203
+
204
+ if (get_all_yaxis_annotations[yaxis] !== undefined) {
205
+ get_all_yaxis_annotations[yaxis].map((annotation) => {
206
+ if (annotation.ay !== undefined) {
207
+ const yshift = annotation.ay;
208
+ const yshift_new = Math.min(
209
+ Math.max(yshift, y_min + y_range * 0.2),
210
+ y_max - y_range * 0.2,
211
+ );
212
+
213
+ if (to_update.annotations === undefined) {
214
+ to_update.annotations = graphs.layout.annotations;
215
+ }
216
+
217
+ to_update.annotations[annotation.index].ay = yshift_new;
218
+ }
219
+ });
220
+ }
221
+ }
222
+ });
223
+
224
+ graphs.layout = merge(graphs.layout, to_update);
225
+
226
+ return { to_update: graphs.layout, yaxis_fixedrange };
227
+ }
228
+ } catch (e) {
229
+ console.log(`Error in AutoScaling: ${e}`);
230
+ }
231
+ return { to_update: {}, yaxis_fixedrange: [] };
232
+ }
frontend-components/plotly/src/components/ChangeColor.tsx ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ //@ts-nocheck
2
+ import { useEffect, useState } from "react";
3
+
4
+ export default function ChangeColor({
5
+ open,
6
+ onColorChange,
7
+ }: {
8
+ open: boolean;
9
+ onColorChange: (color: string) => void;
10
+ }) {
11
+ const [active, setActive] = useState(false);
12
+
13
+ function onChangeColor(color) {
14
+ onColorChange(color);
15
+ }
16
+
17
+ if (open && !active) {
18
+ setActive(true);
19
+ }
20
+ if (!open && active) {
21
+ setActive(false);
22
+ }
23
+
24
+ useEffect(() => {
25
+ if (active) {
26
+ let color_picker = document.getElementById("changecolor");
27
+ color_picker.style.display = "block";
28
+ color_picker.style.width = null;
29
+ dragElement(color_picker);
30
+
31
+ function dragElement(elmnt) {
32
+ let pos1 = 0,
33
+ pos2 = 0,
34
+ pos3 = 0,
35
+ pos4 = 0;
36
+ if (document.getElementById(elmnt.id + "_header")) {
37
+ // if present, the header is where you move the DIV from:
38
+ document.getElementById(elmnt.id + "_header").onmousedown =
39
+ dragMouseDown;
40
+ } else {
41
+ // otherwise, move the DIV from anywhere inside the DIV:
42
+ elmnt.onmousedown = dragMouseDown;
43
+ }
44
+
45
+ function dragMouseDown(e) {
46
+ e = e || window.event;
47
+ e.preventDefault();
48
+ // get the mouse cursor position at startup:
49
+ pos3 = e.clientX;
50
+ pos4 = e.clientY;
51
+ document.onmouseup = closeDragElement;
52
+ // call a function whenever the cursor moves:
53
+ document.onmousemove = elementDrag;
54
+ }
55
+
56
+ function elementDrag(e) {
57
+ e = e || window.event;
58
+ e.preventDefault();
59
+ // calculate the new cursor position:
60
+ pos1 = pos3 - e.clientX;
61
+ pos2 = pos4 - e.clientY;
62
+ pos3 = e.clientX;
63
+ pos4 = e.clientY;
64
+ // set the element's new position:
65
+ elmnt.style.top = elmnt.offsetTop - pos2 + "px";
66
+ elmnt.style.left = elmnt.offsetLeft - pos1 + "px";
67
+ }
68
+
69
+ function closeDragElement() {
70
+ // stop moving when mouse button is released:
71
+ document.onmouseup = null;
72
+ document.onmousemove = null;
73
+ }
74
+ }
75
+ } else {
76
+ document.getElementById("changecolor").style.display = "none";
77
+ }
78
+ }, [active]);
79
+
80
+ return (
81
+ <div id="changecolor">
82
+ <div id="changecolor_header">
83
+ <input
84
+ type="color"
85
+ id="picked_color"
86
+ value="#00ACFF"
87
+ onChange={(e) => {
88
+ let color = e.target.value;
89
+ onChangeColor(color);
90
+ }}
91
+ />
92
+ </div>
93
+ </div>
94
+ );
95
+ }
frontend-components/plotly/src/components/Chart.tsx ADDED
@@ -0,0 +1,902 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // @ts-nocheck
2
+ import clsx from "clsx";
3
+ import { debounce } from "lodash";
4
+ import * as Plotly from "plotly.js-dist-min";
5
+ import { Icons as PlotlyIcons } from "plotly.js-dist-min";
6
+ import React, { useCallback, useEffect, useMemo, useState } from "react";
7
+ import createPlotlyComponent from "react-plotly.js/factory";
8
+ import { init_annotation } from "../utils/addAnnotation";
9
+ import { non_blocking } from "../utils/utils";
10
+ import autoScaling, { isoDateRegex } from "./AutoScaling";
11
+ import ChangeColor from "./ChangeColor";
12
+ import { DARK_CHARTS_TEMPLATE, ICONS, LIGHT_CHARTS_TEMPLATE } from "./Config";
13
+ import AlertDialog from "./Dialogs/AlertDialog";
14
+ import OverlayChartDialog from "./Dialogs/OverlayChartDialog";
15
+ import TextChartDialog from "./Dialogs/TextChartDialog";
16
+ import TitleChartDialog from "./Dialogs/TitleChartDialog";
17
+ import { PlotConfig, hideModebar, ChartHotkeys } from "./PlotlyConfig";
18
+ import ResizeHandler from "./ResizeHandler";
19
+
20
+ // Add logging to help debug why annotations aren't working
21
+ console.log = ((oldLog) => {
22
+ return function(...args) {
23
+ if (args[0] === "plotly_click") {
24
+ console.trace("plotly_click called with:", args[1]);
25
+ }
26
+ return oldLog.apply(console, args);
27
+ };
28
+ })(console.log);
29
+
30
+ const Plot = createPlotlyComponent(Plotly);
31
+ class PlotComponent extends React.Component {
32
+ constructor(props) {
33
+ super(props);
34
+ this.state = {
35
+ data: props.data,
36
+ layout: props.layout,
37
+ frames: props.frames,
38
+ config: props.config,
39
+ useResizeHandler: props.useResizeHandler,
40
+ style: props.style,
41
+ className: props.className,
42
+ divId: props.divId,
43
+ revision: props.revision,
44
+ graphDiv: props.graphDiv,
45
+ debug: props.debug,
46
+ onInitialized: props.onInitialized,
47
+ };
48
+ }
49
+
50
+ render() {
51
+ return (
52
+ <Plot
53
+ data={this.state.data}
54
+ layout={this.state.layout}
55
+ frames={this.state.frames}
56
+ config={this.state.config}
57
+ useResizeHandler={this.state.useResizeHandler}
58
+ style={this.state.style}
59
+ className={this.state.className}
60
+ divId={this.state.divId}
61
+ revision={this.state.revision}
62
+ graphDiv={this.state.graphDiv}
63
+ debug={this.state.debug}
64
+ onInitialized={this.state.onInitialized}
65
+ onUpdate={(figure) => this.setState(figure)}
66
+ onRelayout={(figure) => this.setState(figure)}
67
+ onPurge={(figure) => this.setState(figure)}
68
+ />
69
+ );
70
+ }
71
+ }
72
+
73
+ // Check if a chart is a scatter plot to handle annotations differently
74
+ function isScatterPlot(data) {
75
+ if (!data || !data.data) return false;
76
+
77
+ // Check if chart is primarily scatter plots
78
+ return data.data.some(trace =>
79
+ (trace.type === 'scatter' || trace.mode === 'markers' || trace.mode === 'lines+markers') &&
80
+ !(trace.type === 'candlestick' || trace.type === 'ohlc')
81
+ );
82
+ }
83
+
84
+ // Debug function to check annotation structure
85
+ function debugAnnotation(annotation, label = "Annotation Debug") {
86
+ console.log(`[${label}]`, {
87
+ text: annotation.text,
88
+ visible: annotation.visible,
89
+ x: annotation.x,
90
+ y: annotation.y,
91
+ layer: annotation.layer,
92
+ font: annotation.font,
93
+ arrowcolor: annotation.arrowcolor
94
+ });
95
+ }
96
+
97
+ // Exported for external use
98
+ window.debugPlotlyAnnotations = function() {
99
+ if (window.Plotly && window.Plotly.d3.select('#plotlyChart').node()._fullLayout) {
100
+ const annotations = window.Plotly.d3.select('#plotlyChart').node()._fullLayout.annotations || [];
101
+ console.log("[All Annotations]", annotations);
102
+ annotations.forEach(a => debugAnnotation(a));
103
+ return annotations;
104
+ }
105
+ return [];
106
+ }
107
+
108
+ export const getXRange = (min: string, max: string) => {
109
+ if (isoDateRegex.test(min.replace(" ", "T").split(".")[0])) {
110
+ const check_min = new Date(min.replace(" ", "T").split(".")[0]);
111
+ const check_max = new Date(max.replace(" ", "T").split(".")[0]);
112
+ check_min.setSeconds(0);
113
+ check_max.setSeconds(0);
114
+ check_min.setMilliseconds(0);
115
+ check_max.setMilliseconds(0);
116
+
117
+ const multiplier =
118
+ [5, 0, 1].includes(check_min.getDay()) ||
119
+ [4, 5, 6].includes(check_max.getDay())
120
+ ? 2
121
+ : 0;
122
+
123
+ const x0_min = new Date(check_min.getTime() - 86400000 * multiplier);
124
+ const x1_max = new Date(check_max.getTime() + 86400000 * multiplier);
125
+
126
+ const xrange = [x0_min.toISOString(), x1_max.toISOString()];
127
+ return { x0_min, x1_max, xrange };
128
+ }
129
+
130
+ return { x0_min: min, x1_max: max, xrange: [min, max] };
131
+ };
132
+
133
+ function CreateDataXrange(figure: Figure, xrange?: any) {
134
+ if (figure.frames && figure.frames.length > 0) {
135
+ // Don't filter data for animated charts
136
+ return figure;
137
+ }
138
+ const new_figure = { ...figure };
139
+ const data = new_figure.data;
140
+ if (!xrange) {
141
+ xrange = [
142
+ data[0]?.x[data[0].x.length - 2000],
143
+ data[0]?.x[data[0].x.length - 1],
144
+ ];
145
+ }
146
+ const { x0_min, x1_max, range } = getXRange(xrange[0], xrange[1]);
147
+ xrange = range;
148
+
149
+ const new_data = [];
150
+ data.forEach((trace) => {
151
+ const new_trace = { ...trace };
152
+ const data_keys = [
153
+ "x",
154
+ "y",
155
+ "low",
156
+ "high",
157
+ "open",
158
+ "close",
159
+ "text",
160
+ "customdata",
161
+ ];
162
+ const xaxis: any[] = trace.x ? trace.x : [];
163
+ const chunks = [];
164
+ for (let i = 0; i < xaxis.length; i++) {
165
+ const xval = xaxis[i];
166
+
167
+ if (isoDateRegex.test(xval)) {
168
+ const x_time = new Date(xval).getTime();
169
+ if (x_time >= x0_min.getTime() && x_time <= x1_max.getTime()) {
170
+ chunks.push(i);
171
+ }
172
+ } else if (xval >= xrange[0] && xval <= xrange[1]) {
173
+ chunks.push(i);
174
+ }
175
+ }
176
+ data_keys.forEach((key) => {
177
+ if (trace[key] !== undefined && Array.isArray(trace[key])) {
178
+ new_trace[key] = trace[key].filter((_, i) => chunks.includes(i));
179
+ }
180
+ });
181
+ const color_keys = ["marker", "line"];
182
+ color_keys.forEach((key) => {
183
+ if (trace[key]?.color && Array.isArray(trace[key].color)) {
184
+ new_trace[key] = { ...trace[key] };
185
+ new_trace[key].color = trace[key].color.filter((_, i) =>
186
+ chunks.includes(i),
187
+ );
188
+ }
189
+ });
190
+
191
+ if (chunks.length > 0) new_data.push(new_trace);
192
+ });
193
+
194
+ if (new_data.length === 0)
195
+ return {
196
+ ...figure,
197
+ layout: {
198
+ ...figure.layout,
199
+ xaxis: { ...figure.layout.xaxis, range: xrange },
200
+ },
201
+ };
202
+
203
+ new_figure.layout.xaxis.range = xrange;
204
+ new_figure.data = new_data;
205
+ return new_figure;
206
+ }
207
+
208
+ async function DynamicLoad({
209
+ event,
210
+ figure,
211
+ }: {
212
+ event?: any;
213
+ figure: any;
214
+ }) {
215
+ if (figure.frames && figure.frames.length > 0) {
216
+ // Don't filter data for animated charts
217
+ return figure;
218
+ }
219
+ try {
220
+ const XDATA = figure.data.filter(
221
+ (trace) =>
222
+ trace.x !== undefined && trace.x.length > 0 && trace.x[0] !== undefined,
223
+ );
224
+
225
+ if (XDATA.length === 0) return figure;
226
+ // We get the xaxis range, if no event is passed, we get the last 1000 points
227
+ const xaxis_range = event
228
+ ? [event["xaxis.range[0]"], event["xaxis.range[1]"]]
229
+ : [
230
+ XDATA[0]?.x[XDATA[0].x.length - 1000],
231
+ XDATA[0]?.x[XDATA[0].x.length - 1],
232
+ ];
233
+
234
+ figure = CreateDataXrange(figure, xaxis_range);
235
+
236
+ return figure;
237
+ } catch (e) {
238
+ console.log("error", e);
239
+ }
240
+ }
241
+
242
+ function formatDate(date) {
243
+ const d = new Date(date);
244
+ const month = `${d.getMonth() + 1}`.padStart(2, "0");
245
+ const day = `${d.getDate()}`.padStart(2, "0");
246
+ const year = d.getFullYear();
247
+ const hour = `${d.getHours()}`.padStart(2, "0");
248
+ const minute = `${d.getMinutes()}`.padStart(2, "0");
249
+ const second = `${d.getSeconds()}`.padStart(2, "0");
250
+ return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
251
+ }
252
+
253
+ function Chart({
254
+ json,
255
+ date,
256
+ cmd,
257
+ title,
258
+ globals,
259
+ theme,
260
+ }: {
261
+ // @ts-ignore
262
+ json: Figure;
263
+ date: Date;
264
+ cmd: string;
265
+ title: string;
266
+ globals: any;
267
+ theme: string;
268
+ }) {
269
+ json.layout.width = undefined;
270
+ json.layout.height = undefined;
271
+ if (json.layout?.title?.text) {
272
+ json.layout.title.text = "";
273
+ }
274
+
275
+ const [originalData, setOriginalData] = useState(json);
276
+ const [barButtons, setModeBarButtons] = useState({});
277
+ const [LogYaxis, setLogYaxis] = useState(false);
278
+ const [chartTitle, setChartTitle] = useState(title);
279
+ const [axesTitles, setAxesTitles] = useState({});
280
+ const [plotLoaded, setPlotLoaded] = useState(false);
281
+ const [modal, setModal] = useState({ name: "" });
282
+ const [loading, setLoading] = useState(false);
283
+ const [plotDiv, setPlotDiv] = useState(null);
284
+ const [volumeBars, setVolumeBars] = useState({ old_nticks: {} });
285
+ const [maximizePlot, setMaximizePlot] = useState(false);
286
+ const [dateSliced, setDateSliced] = useState(false);
287
+
288
+ const [plotData, setPlotDataState] = useState(originalData);
289
+ const [annotations, setAnnotations] = useState([]);
290
+ const [changeTheme, setChangeTheme] = useState(false);
291
+ const [darkMode, setDarkMode] = useState(true);
292
+ const [autoScale, setAutoScaling] = useState(false);
293
+ const [changeColor, setChangeColor] = useState(false);
294
+ const [colorActive, setColorActive] = useState(false);
295
+ const [onAnnotationClick, setOnAnnotationClick] = useState({});
296
+ const [ohlcAnnotation, setOhlcAnnotation] = useState([]);
297
+ const [yaxisFixedRange, setYaxisFixedRange] = useState([]); function setPlotData(data: any) {
298
+ data.layout.datarevision = data.layout.datarevision
299
+ ? data.layout.datarevision + 1
300
+ : 1;
301
+
302
+ setPlotDataState(data);
303
+ if (plotDiv && plotData) {
304
+ Plotly.react(plotDiv, data.data, data.layout);
305
+ }
306
+ }
307
+
308
+ const onClose = () => setModal({ name: "" });
309
+
310
+ // @ts-ignore
311
+ const onDeleteAnnotation = useCallback(
312
+ (annotation) => {
313
+ console.log("onDeleteAnnotation", annotation);
314
+ const index = plotData?.layout?.annotations?.findIndex(
315
+ (a: any) => a.text === annotation.text,
316
+ );
317
+ console.log("index", index);
318
+ if (index > -1) {
319
+ plotData?.layout?.annotations?.splice(index, 1);
320
+ setPlotData({ ...plotData });
321
+ setAnnotations(plotData?.layout?.annotations);
322
+ }
323
+ },
324
+ [plotData],
325
+ ); // @ts-ignore
326
+ const onAddAnnotation = useCallback(
327
+ (data) => {
328
+ console.log("onAddAnnotation being called with data:", data);
329
+
330
+ // Use the standard annotation flow
331
+ init_annotation({
332
+ plotData,
333
+ popupData: data,
334
+ setPlotData,
335
+ setModal,
336
+ setOnAnnotationClick,
337
+ setAnnotations,
338
+ onAnnotationClick,
339
+ ohlcAnnotation,
340
+ setOhlcAnnotation,
341
+ annotations,
342
+ plotDiv,
343
+ });
344
+ },
345
+ [plotData, onAnnotationClick, ohlcAnnotation, annotations, plotDiv],
346
+ ); useEffect(() => {
347
+ if (axesTitles && Object.keys(axesTitles).length > 0) {
348
+ const layoutUpdate = {};
349
+ // Update the layout with the new titles
350
+ Object.keys(axesTitles).forEach((k) => {
351
+ plotData.layout[k].title = {
352
+ ...(plotData.layout[k].title || {}),
353
+ text: axesTitles[k],
354
+ };
355
+ plotData.layout[k].showticklabels = true;
356
+ layoutUpdate[`${k}.title.text`] = axesTitles[k];
357
+ });
358
+
359
+ if (plotDiv && Object.keys(layoutUpdate).length > 0) {
360
+ Plotly.relayout(plotDiv, layoutUpdate);
361
+ }
362
+
363
+ setAxesTitles({});
364
+ }
365
+ }, [axesTitles, plotDiv]);
366
+
367
+ function onChangeColor(color) {
368
+ // updates the color of the last added shape
369
+ // this function is called when the color picker is used
370
+ // if there are no shapes, we remove the color picker
371
+ const shapes = plotDiv.layout.shapes;
372
+ if (!shapes || shapes.length === 0) {
373
+ return;
374
+ }
375
+ // we change last added shape color
376
+ const last_shape = shapes[shapes.length - 1];
377
+ last_shape.line.color = color;
378
+ Plotly.update(plotDiv, {}, { shapes: shapes });
379
+ }
380
+
381
+ function button_pressed(title, active = false) {
382
+ // changes the style of the button when it is pressed
383
+ // title is the title of the button
384
+ // active is true if the button is active, false otherwise
385
+
386
+ const button =
387
+ barButtons[title] || document.querySelector(`[data-title="${title}"]`);
388
+ if (!active) {
389
+ button.style.border = "1px solid rgba(0, 151, 222, 1.0)";
390
+ button.style.borderRadius = "5px";
391
+ button.style.borderpadding = "5px";
392
+ button.style.boxShadow = "0 0 5px rgba(0, 151, 222, 1.0)";
393
+ } else {
394
+ button.style.border = "transparent";
395
+ button.style.boxShadow = "none";
396
+ }
397
+ setModeBarButtons({ ...barButtons, [title]: button });
398
+ }
399
+
400
+ const debouncedDynamicLoad = async (eventData, figure) => {
401
+ if (dateSliced) {
402
+ const data = { ...figure };
403
+ DynamicLoad({
404
+ event: eventData,
405
+ figure: data,
406
+ }).then(async (toUpdate) => {
407
+ autoScaling(eventData, toUpdate).then((scaled) => {
408
+ if (!scaled.to_update) return;
409
+ setYaxisFixedRange(scaled.yaxis_fixedrange);
410
+ setPlotData({ ...toUpdate, layout: scaled.to_update });
411
+ });
412
+ });
413
+ } else {
414
+ const scaled = await autoScaling(eventData, figure);
415
+ if (!scaled.to_update) return;
416
+ setYaxisFixedRange(scaled.yaxis_fixedrange);
417
+ setPlotData({ ...figure, layout: scaled.to_update });
418
+ }
419
+ };
420
+
421
+ const autoscaleButton = useCallback(() => {
422
+ // We need to check if the button is active or not
423
+ const title = "Auto Scale (Ctrl+Shift+A)";
424
+ const button =
425
+ barButtons[title] || document.querySelector(`[data-title="${title}"]`);
426
+ let active = true;
427
+
428
+ if (button.style.border === "transparent") {
429
+ plotDiv.removeAllListeners("plotly_relayout");
430
+ active = false;
431
+ plotDiv.on("plotly_relayout", async (eventdata) => {
432
+ if (eventdata["xaxis.range[0]"] === undefined) return;
433
+ const debounceTimer = eventdata["relayout"] ? 0 : 300;
434
+ if (
435
+ !eventdata["relayout"] &&
436
+ isoDateRegex.test(
437
+ eventdata["xaxis.range[0]"].toString().replace(" ", "T"),
438
+ )
439
+ ) {
440
+ const date1 = new Date(eventdata["xaxis.range[0]"].replace(" ", "T"));
441
+ const date2 = new Date(eventdata["xaxis.range[1]"].replace(" ", "T"));
442
+
443
+ if (date2.getTime() - date1.getTime() < 3600000 * 2) {
444
+ const d1 = new Date(date1.getTime() - 3600000 * 2);
445
+ const d2 = new Date(date2.getTime() + 3600000 * 2);
446
+
447
+ eventdata["xaxis.range[0]"] = formatDate(d1);
448
+ eventdata["xaxis.range[1]"] = formatDate(d2);
449
+ eventdata["relayout"] = true;
450
+ return Plotly.relayout(plotDiv, eventdata);
451
+ }
452
+ }
453
+ debounce(async () => {
454
+ debouncedDynamicLoad(eventdata, originalData);
455
+ }, debounceTimer)();
456
+ });
457
+ }
458
+ // If the button isn't active, we remove the listener so
459
+ // the graphs don't autoscale anymore
460
+ else {
461
+ plotDiv.removeAllListeners("plotly_relayout");
462
+ yaxisFixedRange.forEach((yaxis) => {
463
+ plotDiv.layout[yaxis].fixedrange = false;
464
+ });
465
+ setYaxisFixedRange([]);
466
+ if (dateSliced) {
467
+ plotDiv.on(
468
+ "plotly_relayout",
469
+ debounce(async (eventdata) => {
470
+ if (eventdata["xaxis.range[0]"] === undefined) return;
471
+ debouncedDynamicLoad(eventdata, originalData);
472
+ }, 300),
473
+ );
474
+ }
475
+ }
476
+
477
+ button_pressed(title, active);
478
+ }, [
479
+ barButtons,
480
+ dateSliced,
481
+ debouncedDynamicLoad,
482
+ originalData,
483
+ plotDiv,
484
+ yaxisFixedRange,
485
+ ]);
486
+
487
+ function changecolorButton() {
488
+ // We need to check if the button is active or not
489
+ const title = "Edit Color (Ctrl+E)";
490
+ const button =
491
+ barButtons[title] || document.querySelector(`[data-title="${title}"]`);
492
+ let active = true;
493
+
494
+ if (button.style.border === "transparent") {
495
+ active = false;
496
+ }
497
+
498
+ setColorActive(!active);
499
+ button_pressed(title, active);
500
+ }
501
+
502
+ useEffect(() => {
503
+ if (autoScale) {
504
+ const scale = !autoScale;
505
+ console.log("activateAutoScale", scale);
506
+ autoscaleButton();
507
+ setAutoScaling(false);
508
+ }
509
+ }, [autoScale]);
510
+
511
+ useEffect(() => {
512
+ if (changeColor) {
513
+ changecolorButton();
514
+ setChangeColor(false);
515
+ }
516
+ }, [changeColor]);
517
+
518
+ useEffect(() => {
519
+ if (changeTheme) {
520
+ try {
521
+ console.log("changeTheme", changeTheme);
522
+ const TRACES = originalData?.data.filter(
523
+ (trace) => trace?.name?.trim() === "Volume",
524
+ );
525
+ const darkmode = !darkMode;
526
+
527
+ window.document.body.style.backgroundColor = darkmode ? "#000" : "#fff";
528
+
529
+ originalData.layout.font = {
530
+ ...(originalData.layout.font || {}),
531
+ color: darkmode ? "#fff" : "#000",
532
+ };
533
+
534
+ const changeIcon = darkmode ? ICONS.sunIcon : ICONS.moonIcon;
535
+
536
+ document
537
+ .querySelector('[data-title="Change Theme"]')
538
+ .getElementsByTagName("path")[0]
539
+ .setAttribute("d", changeIcon.path);
540
+
541
+ document
542
+ .querySelector('[data-title="Change Theme"]')
543
+ .getElementsByTagName("svg")[0]
544
+ .setAttribute("viewBox", changeIcon.viewBox);
545
+
546
+ const volumeColorsDark = {
547
+ "#00ACFF0": "#00ACFF",
548
+ "#e4003a": "#e4003a",
549
+ };
550
+ const volumeColorsLight = {
551
+ "#e4003a": "#e4003a",
552
+ "#00ACFF": "#00ACFF",
553
+ };
554
+
555
+ const volumeColors = darkmode ? volumeColorsDark : volumeColorsLight;
556
+
557
+ TRACES.forEach((trace) => {
558
+ if (trace.type === "bar" && Array.isArray(trace.marker.color))
559
+ trace.marker.color = trace.marker.color.map((color) => {
560
+ return volumeColors[color] || color;
561
+ });
562
+ });
563
+ originalData.layout.template = darkmode
564
+ ? DARK_CHARTS_TEMPLATE
565
+ : LIGHT_CHARTS_TEMPLATE;
566
+
567
+ // Preserve existing annotations as-is (no modifications)
568
+ if (plotData.layout.annotations && plotData.layout.annotations.length > 0) {
569
+ originalData.layout.annotations = [...plotData.layout.annotations];
570
+ }
571
+
572
+ setPlotData({ ...originalData });
573
+ setDarkMode(darkmode);
574
+ setChangeTheme(false);
575
+ } catch (e) {
576
+ console.log("error", e);
577
+ }
578
+ }
579
+ }, [changeTheme, plotData.layout.annotations]);
580
+
581
+ useEffect(() => {
582
+ if (plotLoaded) {
583
+ setDarkMode(true);
584
+ setAutoScaling(false);
585
+ const captureButtons = [
586
+ "Overlay chart from CSV",
587
+ "Add Text",
588
+ "Change Titles",
589
+ "Auto Scale (Ctrl+Shift+A)",
590
+ "Reset Axes",
591
+ ];
592
+ const autoscale = document.querySelector('[data-title="Autoscale"]');
593
+ if (autoscale) {
594
+ autoscale
595
+ .getElementsByTagName("path")[0]
596
+ .setAttribute("d", PlotlyIcons.home.path);
597
+ autoscale.setAttribute("data-title", "Reset Axes");
598
+ }
599
+
600
+ window.MODEBAR = document.getElementsByClassName(
601
+ "modebar-container",
602
+ )[0] as HTMLElement;
603
+ const modeBarButtons = window.MODEBAR.getElementsByClassName(
604
+ "modebar-btn",
605
+ ) as HTMLCollectionOf<HTMLElement>;
606
+
607
+ window.MODEBAR.style.cssText = `${window.MODEBAR.style.cssText}; display:flex;`;
608
+
609
+ // Add annotation click handler to ensure editing works on scatter plots
610
+ if (plotDiv) {
611
+ // When an annotation is clicked, open the edit dialog
612
+ plotDiv.on('plotly_clickannotation', function(data) {
613
+ console.log("Annotation clicked:", data);
614
+ if (data && data.annotation && data.annotation.text) {
615
+ setModal({
616
+ name: "textDialog",
617
+ data: {
618
+ annotation_dict: data.annotation,
619
+ mode: "edit"
620
+ }
621
+ });
622
+ }
623
+ });
624
+ }
625
+
626
+ if (modeBarButtons) {
627
+ const barbuttons: any = {};
628
+ for (let i = 0; i < modeBarButtons.length; i++) {
629
+ const btn = modeBarButtons[i];
630
+ if (captureButtons.includes(btn.getAttribute("data-title"))) {
631
+ btn.classList.add("ph-capture");
632
+ }
633
+ btn.style.border = "transparent";
634
+ barbuttons[btn.getAttribute("data-title")] = btn;
635
+ }
636
+ setModeBarButtons(barbuttons);
637
+ }
638
+
639
+ if (plotData?.layout?.yaxis?.type !== undefined) {
640
+ if (plotData.layout.yaxis.type === "log" && !LogYaxis) {
641
+ console.log("yaxis.type changed to log");
642
+ setLogYaxis(true);
643
+ }
644
+ if (plotData.layout.yaxis.type === "linear" && LogYaxis) {
645
+ console.log("yaxis.type changed to linear");
646
+ setLogYaxis(false);
647
+
648
+ // We update the yaxis exponent format to none,
649
+ // set the tickformat to null and the exponentbase to 10
650
+ const layout_update = {
651
+ "yaxis.exponentformat": "none",
652
+ "yaxis.tickformat": null,
653
+ "yaxis.exponentbase": 10,
654
+ };
655
+ Plotly.update(plotDiv, {}, layout_update);
656
+ }
657
+ }
658
+
659
+ window.addEventListener("resize", async function () {
660
+ const update = await ResizeHandler({
661
+ plotData,
662
+ volumeBars,
663
+ setMaximizePlot,
664
+ });
665
+ const layout_update = update.layout_update;
666
+ const newPlotData = update.plotData;
667
+ const volume_update = update.volume_update;
668
+
669
+ if (Object.keys(layout_update).length > 0) {
670
+ setPlotData(newPlotData);
671
+ setVolumeBars(volume_update);
672
+ Plotly.update(plotDiv, {}, layout_update);
673
+ }
674
+ });
675
+
676
+ if (theme !== "dark") {
677
+ setChangeTheme(true);
678
+ }
679
+ }
680
+ }, [plotLoaded]);
681
+
682
+ useEffect(() => {
683
+ // This effect ensures annotations appear correctly on all chart types
684
+ if (plotDiv && plotData?.layout?.annotations?.length > 0) {
685
+ Plotly.relayout(plotDiv, {'annotations': plotData.layout.annotations});
686
+ }
687
+ }, [plotData.layout.annotations, plotDiv]); const plotComponent = useMemo(
688
+ () => (
689
+ <PlotComponent
690
+ onInitialized={(_figure, graphDiv) => {
691
+ if (!plotDiv) {
692
+ if (graphDiv) {
693
+ graphDiv.globals = globals;
694
+ setPlotDiv(graphDiv);
695
+ graphDiv.on('plotly_clickannotation', function(data) {
696
+ if (data && data.annotation && data.annotation.text) {
697
+ setModal({
698
+ name: "textDialog",
699
+ data: {
700
+ annotation_dict: data.annotation,
701
+ mode: "edit"
702
+ }
703
+ });
704
+ }
705
+ });
706
+ }
707
+ }
708
+ if (!plotLoaded) setPlotLoaded(true);
709
+ }}
710
+ className="w-full h-full"
711
+ divId="plotlyChart"
712
+ data={plotData.data}
713
+ layout={plotData.layout}
714
+ frames={plotData.frames}
715
+ config={PlotConfig({
716
+ setModal: setModal,
717
+ changeTheme: setChangeTheme,
718
+ autoScaling: setAutoScaling,
719
+ Loading: setLoading,
720
+ changeColor: setChangeColor,
721
+ })}
722
+ />
723
+ ),
724
+ [
725
+ plotDiv,
726
+ originalData,
727
+ plotLoaded,
728
+ plotData,
729
+ globals,
730
+ setPlotDiv,
731
+ setPlotLoaded,
732
+ setModal,
733
+ setChangeTheme,
734
+ setAutoScaling,
735
+ setLoading,
736
+ onChangeColor,
737
+ ],
738
+ );
739
+
740
+ const memoizedAlertDialog = useMemo(() => {
741
+ return (
742
+ <AlertDialog
743
+ title={modal?.data?.title}
744
+ content={modal?.data?.content}
745
+ open={modal?.name === "alertDialog"}
746
+ close={onClose}
747
+ />
748
+ );
749
+ }, [modal, onClose]);
750
+
751
+ const memoizedOverlayChartDialog = useMemo(() => {
752
+ return (
753
+ <OverlayChartDialog
754
+ addOverlay={(overlay) => {
755
+ console.log(overlay);
756
+ overlay.layout.showlegend = true;
757
+ setOriginalData(overlay);
758
+ setPlotData(overlay);
759
+ }}
760
+ plotlyData={originalData}
761
+ setLoading={setLoading}
762
+ open={modal?.name === "overlayChart"}
763
+ close={onClose}
764
+ />
765
+ );
766
+ }, [modal, plotData, onClose, setPlotData, setLoading]);
767
+
768
+ const memoizedTitleChartDialog = useMemo(() => {
769
+ return (
770
+ <TitleChartDialog
771
+ updateTitle={(title) => setChartTitle(title)}
772
+ updateAxesTitles={(axesTitles) => setAxesTitles(axesTitles)}
773
+ defaultTitle={chartTitle}
774
+ plotlyData={plotData}
775
+ open={modal?.name === "titleDialog"}
776
+ close={onClose}
777
+ />
778
+ );
779
+ }, [modal, plotData, chartTitle, onClose]);
780
+
781
+ const memoizedTextChartDialog = useMemo(() => {
782
+ return (
783
+ <TextChartDialog
784
+ popupData={modal?.name === "textDialog" ? modal?.data : null}
785
+ open={modal?.name === "textDialog"}
786
+ close={onClose}
787
+ addAnnotation={(data) => onAddAnnotation(data)}
788
+ deleteAnnotation={(data) => onDeleteAnnotation(data)}
789
+ />
790
+ );
791
+ }, [
792
+ modal,
793
+ onAddAnnotation,
794
+ onDeleteAnnotation,
795
+ onClose,
796
+ plotData,
797
+ setPlotData,
798
+ ]);
799
+
800
+ const memoizedChangeColor = useMemo(() => {
801
+ return <ChangeColor open={colorActive} onColorChange={onChangeColor} />;
802
+ }, [colorActive, onChangeColor]);
803
+
804
+ const memoizedChartHotkeys = useMemo(() => {
805
+ return (
806
+ <ChartHotkeys
807
+ setModal={setModal}
808
+ Loading={setLoading}
809
+ changeColor={setChangeColor}
810
+ />
811
+ );
812
+ }, [setModal, setLoading, setChangeColor]);
813
+
814
+ return (
815
+ <div className="relative h-full">
816
+ {loading && (
817
+ <div className="absolute inset-0 flex items-center justify-center z-[100]">
818
+ <svg
819
+ className="animate-spin h-20 w-20 text-white"
820
+ xmlns="http://www.w3.org/2000/svg"
821
+ fill="none"
822
+ viewBox="0 0 24 24"
823
+ >
824
+ <circle
825
+ className="opacity-25"
826
+ cx="12"
827
+ cy="12"
828
+ r="10"
829
+ stroke="currentColor"
830
+ strokeWidth="4"
831
+ />
832
+ <path
833
+ className="opacity-75"
834
+ fill="currentColor"
835
+ d="M4 12a8 8 0 018-8v8z"
836
+ />
837
+ </svg>
838
+ </div>
839
+ )}
840
+ <div id="loading" className="saving">
841
+ <div id="loading_text" className="loading_text" />
842
+ <div id="loader" className="loader" />
843
+ </div>
844
+ {memoizedAlertDialog}
845
+ {memoizedOverlayChartDialog}
846
+ {memoizedTitleChartDialog}
847
+ {memoizedTextChartDialog}
848
+ {memoizedChangeColor}
849
+ {memoizedChartHotkeys}
850
+
851
+ <div className="relative h-full" id="MainChart">
852
+ <div className="_header relative gap-4 py-2 text-center text-xs flex items-center justify-between px-4 text-white">
853
+ <div className="w-1/3">
854
+ <svg
855
+ xmlns="http://www.w3.org/2000/svg"
856
+ width="64"
857
+ height="40"
858
+ fill="none"
859
+ viewBox="0 0 64 40"
860
+ >
861
+ <path
862
+ fill="#fff"
863
+ d="M61.283 3.965H33.608v27.757h25.699V19.826H37.561v-3.965H63.26V3.965h-1.977zM39.538 23.792h15.815v3.965H37.561v-3.965h1.977zM59.306 9.913v1.983H37.561V7.931h21.745v1.982zM33.606 0h-3.954v3.965H33.606V0zM25.7 3.966H0V15.86h25.7v3.965H3.953v11.896h25.7V3.966h-3.955zm0 21.808v1.983H7.907v-3.965h17.791v1.982zm0-15.86v1.982H3.953V7.931h21.745v1.982zM37.039 35.693v2.952l-.246-.246-.245-.245-.245-.247-.245-.246-.246-.246-.245-.245-.245-.247-.247-.246-.245-.246-.245-.246-.245-.246-.246-.246h-.49v3.936h.49v-3.198l.246.246.245.246.245.246.245.246.246.246.246.246.245.247.246.245.245.246.245.247.245.246.246.245.245.246h.245v-3.936h-.49zM44.938 37.17h-.491v-1.477h-2.944v3.937h3.93v-2.46h-.495zm-2.944-.246v-.739h1.962v.984h-1.962v-.245zm2.944.984v1.23h-2.944V37.66h2.944v.247zM52.835 37.17h-.49v-1.477h-2.946v3.937h3.925v-2.46h-.489zm-2.944-.246v-.739h1.963v.984h-1.965l.002-.245zm2.944.984v1.23H49.89V37.66h2.946v.247zM29.174 35.693H25.739v3.936H29.663v-.491H26.229v-.984h2.943v-.493H26.229v-1.476h3.434v-.492h-.489zM13.37 35.693H9.934v3.937h3.925v-3.937h-.49zm0 .738v2.709h-2.945v-2.955h2.943l.001.246zM21.276 35.693h-3.435v3.937h.491v-1.476h3.434v-2.461h-.49zm0 .738v1.23h-2.944v-1.476h2.944v.246z"
864
+ />
865
+ </svg>
866
+ </div>
867
+ <p className="font-bold w-1/3 flex flex-col gap-0.5 items-center">
868
+ {chartTitle}
869
+ {/* {source && (
870
+ <span className="font-normal text-[10px]">{`[${source}]`}</span>
871
+ )} */}
872
+ </p>
873
+ <p className="w-1/3 text-right text-xs">
874
+ {new Intl.DateTimeFormat("en-GB", {
875
+ dateStyle: "full",
876
+ timeStyle: "long",
877
+ })
878
+ .format(date)
879
+ .replace(/:\d\d /, " ")}
880
+ <br />
881
+ <span className="text-grey-400">{cmd}</span>
882
+ </p>
883
+ {/* {source && typeof source === "string" && source.includes("*") && (
884
+ <p className="text-[8px] absolute bottom-0 right-4">
885
+ *not affiliated
886
+ </p>
887
+ )} */}
888
+ </div>
889
+ <div
890
+ className={clsx("w-full sm:pb-12", {
891
+ "h-[calc(100%-10px)]": maximizePlot,
892
+ "h-[calc(100%-50px)]": !maximizePlot,
893
+ })}
894
+ >
895
+ {plotComponent}
896
+ </div>
897
+ </div>
898
+ </div>
899
+ );
900
+ }
901
+
902
+ export default React.memo(Chart);
frontend-components/plotly/src/components/Config.tsx ADDED
@@ -0,0 +1,800 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export const ICONS = {
2
+ sunIcon: {
3
+ viewBox: "0 0 16 16",
4
+ width: 16,
5
+ height: 16,
6
+ path: "M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z",
7
+ },
8
+ moonIcon: {
9
+ viewBox: "0 0 25 25",
10
+ width: 25,
11
+ height: 25,
12
+ path: "M21.752 15.002A9.718 9.718 0 0118 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 003 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 009.002-5.998z",
13
+ },
14
+ plotCsv: {
15
+ width: 900,
16
+ height: 900,
17
+ path: "M170.666667 106.666667l0.192 736H906.666667v64H149.546667c-23.552 0-42.666667-19.093333-42.666667-42.666667L106.666667 106.666667h64z m686.506666 454.144l13.653334 16.362666a21.333333 21.333333 0 0 1-2.666667 30.058667l-171.157333 143.146667a21.333333 21.333333 0 0 1-21.546667 3.477333l-229.973333-91.285333-113.834667 94.997333a21.333333 21.333333 0 0 1-30.037333-2.709333l-13.653334-16.362667a21.333333 21.333333 0 0 1 2.688-30.058667l133.312-111.274666a21.333333 21.333333 0 0 1 21.546667-3.456l229.930667 91.264 151.68-126.826667a21.333333 21.333333 0 0 1 30.037333 2.666667z m-1.621333-417.962667l16.896 13.013333a21.333333 21.333333 0 0 1 3.925333 29.888L685.802667 433.706667a21.333333 21.333333 0 0 1-20.202667 8.085333l-226.794667-35.413333-150.186666 222.357333a21.333333 21.333333 0 0 1-27.477334 7.018667l-2.133333-1.28-17.685333-11.946667a21.333333 21.333333 0 0 1-5.738667-29.610667l165.354667-244.821333a21.333333 21.333333 0 0 1 20.992-9.130667L650.453333 374.613333l175.146667-227.882666a21.333333 21.333333 0 0 1 29.930667-3.904z",
18
+ },
19
+ addText: {
20
+ path: "M896 928H128a32 32 0 0 1-32-32V128a32 32 0 0 1 32-32h768a32 32 0 0 1 32 32v768a32 32 0 0 1-32 32z m-736-64h704v-704h-704z M704 352H320a32 32 0 0 1 0-64h384a32 32 0 0 1 0 64z M512 736a32 32 0 0 1-32-32V320a32 32 0 0 1 64 0v384a32 32 0 0 1-32 32z",
21
+ width: 950,
22
+ height: 950,
23
+ },
24
+ changeTitle: {
25
+ path: "M122.368 165.888h778.24c-9.216 0-16.384-7.168-16.384-16.384v713.728c0-9.216 7.168-16.384 16.384-16.384h-778.24c9.216 0 16.384 7.168 16.384 16.384V150.016c0 8.192-6.656 15.872-16.384 15.872z m-32.768 684.544c0 26.112 20.992 47.104 47.104 47.104h750.08c26.112 0 47.104-20.992 47.104-47.104V162.304c0-26.112-20.992-47.104-47.104-47.104H136.704c-26.112 0-47.104 20.992-47.104 47.104v688.128z M244.736 656.896h534.016v62.464H244.736z M373.76 358.4H307.2v219.136h-45.568V358.4H192v-41.472H373.76V358.4zM403.968 316.928h44.032v50.176h-44.032v-50.176z m0 67.072h44.032v194.048h-44.032V384zM576.512 541.184l8.704 31.744c-13.312 5.12-26.624 8.192-38.912 8.704-32.768 1.024-48.64-15.36-48.128-48.128V422.912h-26.624V384h26.624v-46.592l44.032-21.504V384h36.352v38.912h-36.352V532.48c-1.024 10.24 3.072 14.848 11.264 13.824 5.12 0 12.8-1.536 23.04-5.12zM619.008 316.928h44.032v260.608h-44.032V316.928zM813.056 509.952l41.472 12.8c-11.776 40.96-37.888 61.44-78.336 60.416-52.736-1.536-80.384-34.304-81.92-98.304 2.56-67.072 29.696-102.4 81.92-105.984 52.224 1.536 78.336 36.864 79.36 105.984v13.824h-117.248c3.584 30.208 15.872 45.568 37.888 46.592 19.968 0.512 32.256-11.264 36.864-35.328z m-72.704-51.712h70.656c-1.024-25.088-12.288-38.4-33.792-38.912-21.504 0.512-33.792 13.824-36.864 38.912z",
26
+ width: 920,
27
+ height: 900,
28
+ },
29
+ changeColor: {
30
+ path: "M8 3C5.79 3 4 4.79 4 7V14C4 15.1 4.9 16 6 16H9V20C9 21.1 9.9 22 11 22H13C14.1 22 15 21.1 15 20V16H18C19.1 16 20 15.1 20 14V3H8M8 5H12V7H14V5H15V9H17V5H18V10H6V7C6 5.9 6.9 5 8 5M6 14V12H18V14H6Z",
31
+ width: 22,
32
+ height: 22,
33
+ },
34
+ uploadImage: {
35
+ path: "M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5",
36
+ width: 1024,
37
+ height: 1024,
38
+ },
39
+ downloadCsv: {
40
+ path: `M486.2,196.121h-13.164V132.59c0-0.399-0.064-0.795-0.116-1.2c-0.021-2.52-0.824-5-2.551-6.96L364.656,3.677
41
+ c-0.031-0.034-0.064-0.044-0.085-0.075c-0.629-0.707-1.364-1.292-2.141-1.796c-0.231-0.157-0.462-0.286-0.704-0.419
42
+ c-0.672-0.365-1.386-0.672-2.121-0.893c-0.199-0.052-0.377-0.134-0.576-0.188C358.229,0.118,357.4,0,356.562,0H96.757
43
+ C84.893,0,75.256,9.649,75.256,21.502v174.613H62.093c-16.972,0-30.733,13.756-30.733,30.73v159.81
44
+ c0,16.966,13.761,30.736,30.733,30.736h13.163V526.79c0,11.854,9.637,21.501,21.501,21.501h354.777
45
+ c11.853,0,21.502-9.647,21.502-21.501V417.392H486.2c16.966,0,30.729-13.764,30.729-30.731v-159.81
46
+ C516.93,209.872,503.166,196.121,486.2,196.121z M96.757,21.502h249.053v110.006c0,5.94,4.818,10.751,10.751,10.751h94.973v53.861
47
+ H96.757V21.502z M258.618,313.18c-26.68-9.291-44.063-24.053-44.063-47.389c0-27.404,22.861-48.368,60.733-48.368
48
+ c18.107,0,31.447,3.811,40.968,8.107l-8.09,29.3c-6.43-3.107-17.862-7.632-33.59-7.632c-15.717,0-23.339,7.149-23.339,15.485
49
+ c0,10.247,9.047,14.769,29.78,22.632c28.341,10.479,41.681,25.239,41.681,47.874c0,26.909-20.721,49.786-64.792,49.786
50
+ c-18.338,0-36.449-4.776-45.497-9.77l7.38-30.016c9.772,5.014,24.775,10.006,40.264,10.006c16.671,0,25.488-6.908,25.488-17.396
51
+ C285.536,325.789,277.909,320.078,258.618,313.18z M69.474,302.692c0-54.781,39.074-85.269,87.654-85.269
52
+ c18.822,0,33.113,3.811,39.549,7.149l-7.392,28.816c-7.38-3.084-17.632-5.939-30.491-5.939c-28.822,0-51.206,17.375-51.206,53.099
53
+ c0,32.158,19.051,52.4,51.456,52.4c10.947,0,23.097-2.378,30.241-5.238l5.483,28.346c-6.672,3.34-21.674,6.919-41.208,6.919
54
+ C98.06,382.976,69.474,348.424,69.474,302.692z M451.534,520.962H96.757v-103.57h354.777V520.962z M427.518,380.583h-42.399
55
+ l-51.45-160.536h39.787l19.526,67.894c5.479,19.046,10.479,37.386,14.299,57.397h0.709c4.048-19.298,9.045-38.352,14.526-56.693
56
+ l20.487-68.598h38.599L427.518,380.583z`,
57
+ width: 550,
58
+ height: 550,
59
+ transform: "translate(4, 0)",
60
+ },
61
+ downloadImage: {
62
+ path: "M22.71,6.29a1,1,0,0,0-1.42,0L20,7.59V2a1,1,0,0,0-2,0V7.59l-1.29-1.3a1,1,0,0,0-1.42,1.42l3,3a1,1,0,0,0,.33.21.94.94,0,0,0,.76,0,1,1,0,0,0,.33-.21l3-3A1,1,0,0,0,22.71,6.29ZM19,13a1,1,0,0,0-1,1v.38L16.52,12.9a2.79,2.79,0,0,0-3.93,0l-.7.7L9.41,11.12a2.85,2.85,0,0,0-3.93,0L4,12.6V7A1,1,0,0,1,5,6h8a1,1,0,0,0,0-2H5A3,3,0,0,0,2,7V19a3,3,0,0,0,3,3H17a3,3,0,0,0,3-3V14A1,1,0,0,0,19,13ZM5,20a1,1,0,0,1-1-1V15.43l2.9-2.9a.79.79,0,0,1,1.09,0l3.17,3.17,0,0L15.46,20Zm13-1a.89.89,0,0,1-.18.53L13.31,15l.7-.7a.77.77,0,0,1,1.1,0L18,17.21Z",
63
+ width: 21,
64
+ height: 21,
65
+ transform: "translate(-2, -2)",
66
+ },
67
+ };
68
+
69
+ export const DARK_CHARTS_TEMPLATE = {
70
+ line: {
71
+ "up_color": "#0074D9",
72
+ "down_color": "#FF4136",
73
+ "color": "#111111",
74
+ "width": 1.5
75
+ },
76
+ data: {
77
+ candlestick: [
78
+ {
79
+ decreasing: {
80
+ fillcolor: "#e4003a",
81
+ line: {
82
+ color: "#e4003a",
83
+ },
84
+ },
85
+ increasing: {
86
+ fillcolor: "#00ACFF",
87
+ line: {
88
+ color: "#00ACFF",
89
+ },
90
+ },
91
+ type: "candlestick",
92
+ },
93
+ ],
94
+ },
95
+ layout: {
96
+ annotationdefaults: {
97
+ showarrow: false,
98
+ },
99
+ autotypenumbers: "strict",
100
+ colorway: [
101
+ "#1f77b4",
102
+ "#ff7f0e",
103
+ "#2ca02c",
104
+ "#d62728",
105
+ "#9467bd",
106
+ "#8c564b",
107
+ "#e377c2",
108
+ "#bcbd22",
109
+ "#17becf",
110
+ "#aec7e8",
111
+ "#ffbb78",
112
+ "#ff9896",
113
+ "#c5b0d5",
114
+ "#f7b6d2",
115
+ "#dbdb8d",
116
+ "#9edae5"
117
+ ],
118
+ dragmode: "pan",
119
+ font: {
120
+ family: "Arial, Helvetica, sans-serif",
121
+ size: 16,
122
+ },
123
+ hoverlabel: {
124
+ align: "left",
125
+ font: {
126
+ family: "Arial, Helvetica, sans-serif",
127
+ size: 14,
128
+ },
129
+ },
130
+ mapbox: {
131
+ style: "dark",
132
+ },
133
+ hovermode: "x",
134
+ legend: {
135
+ bgcolor: "rgba(0, 0, 0, 0)",
136
+ x: 1,
137
+ xanchor: "right",
138
+ y: 0.99,
139
+ yanchor: "bottom",
140
+ font: {
141
+ family: "Arial, Helvetica, sans-serif",
142
+ size: 14,
143
+ },
144
+ },
145
+ paper_bgcolor: "#000000",
146
+ plot_bgcolor: "#000000",
147
+ xaxis: {
148
+ automargin: true,
149
+ autorange: true,
150
+ rangeslider: {
151
+ visible: false,
152
+ },
153
+ showgrid: true,
154
+ showline: true,
155
+ tickfont: {
156
+ family: "Arial, Helvetica, sans-serif",
157
+ size: 14,
158
+ },
159
+ zeroline: false,
160
+ tick0: 1,
161
+ title: {
162
+ standoff: 20,
163
+ text: "",
164
+ font: {
165
+ family: "Arial, Helvetica, sans-serif",
166
+ size: 16,
167
+ },
168
+ },
169
+ gridcolor: "#283442",
170
+ linecolor: "#F5EFF3",
171
+ mirror: true,
172
+ ticks: "outside",
173
+ },
174
+ yaxis: {
175
+ anchor: "x",
176
+ automargin: true,
177
+ fixedrange: false,
178
+ zeroline: false,
179
+ showgrid: true,
180
+ showline: true,
181
+ side: "right",
182
+ tick0: 0.5,
183
+ tickfont: {
184
+ family: "Arial, Helvetica, sans-serif",
185
+ size: 14,
186
+ },
187
+ title: {
188
+ standoff: 20,
189
+ text: "",
190
+ font: {
191
+ family: "Arial, Helvetica, sans-serif",
192
+ size: 16,
193
+ },
194
+ },
195
+ gridcolor: "#283442",
196
+ linecolor: "#F5EFF3",
197
+ mirror: true,
198
+ ticks: "outside",
199
+ },
200
+ },
201
+ };
202
+
203
+ export const LIGHT_CHARTS_TEMPLATE = {
204
+ line: {
205
+ "up_color": "#0074D9",
206
+ "down_color": "#FF4136",
207
+ "color": "#111111",
208
+ "width": 1.5
209
+ },
210
+ data: {
211
+ barpolar: [
212
+ {
213
+ marker: {
214
+ line: {
215
+ color: "white",
216
+ width: 0.5,
217
+ },
218
+ pattern: {
219
+ fillmode: "overlay",
220
+ size: 10,
221
+ solidity: 0.2,
222
+ },
223
+ },
224
+ type: "barpolar",
225
+ },
226
+ ],
227
+ bar: [
228
+ {
229
+ error_x: {
230
+ color: "#2a3f5f",
231
+ },
232
+ error_y: {
233
+ color: "#2a3f5f",
234
+ },
235
+ marker: {
236
+ line: {
237
+ color: "white",
238
+ width: 0.5,
239
+ },
240
+ pattern: {
241
+ fillmode: "overlay",
242
+ size: 10,
243
+ solidity: 0.2,
244
+ },
245
+ },
246
+ type: "bar",
247
+ },
248
+ ],
249
+ carpet: [
250
+ {
251
+ aaxis: {
252
+ endlinecolor: "#2a3f5f",
253
+ gridcolor: "#C8D4E3",
254
+ linecolor: "#C8D4E3",
255
+ minorgridcolor: "#C8D4E3",
256
+ startlinecolor: "#2a3f5f",
257
+ },
258
+ baxis: {
259
+ endlinecolor: "#2a3f5f",
260
+ gridcolor: "#C8D4E3",
261
+ linecolor: "#C8D4E3",
262
+ minorgridcolor: "#C8D4E3",
263
+ startlinecolor: "#2a3f5f",
264
+ },
265
+ type: "carpet",
266
+ },
267
+ ],
268
+ choropleth: [
269
+ {
270
+ colorbar: {
271
+ outlinewidth: 0,
272
+ ticks: "",
273
+ },
274
+ type: "choropleth",
275
+ },
276
+ ],
277
+ contourcarpet: [
278
+ {
279
+ colorbar: {
280
+ outlinewidth: 0,
281
+ ticks: "",
282
+ },
283
+ type: "contourcarpet",
284
+ },
285
+ ],
286
+ contour: [
287
+ {
288
+ colorbar: {
289
+ outlinewidth: 0,
290
+ ticks: "",
291
+ },
292
+ colorscale: [
293
+ [0.0, "#0d0887"],
294
+ [0.1111111111111111, "#46039f"],
295
+ [0.2222222222222222, "#7201a8"],
296
+ [0.3333333333333333, "#9c179e"],
297
+ [0.4444444444444444, "#bd3786"],
298
+ [0.5555555555555556, "#d8576b"],
299
+ [0.6666666666666666, "#ed7953"],
300
+ [0.7777777777777778, "#fb9f3a"],
301
+ [0.8888888888888888, "#fdca26"],
302
+ [1.0, "#f0f921"],
303
+ ],
304
+ type: "contour",
305
+ },
306
+ ],
307
+ heatmap: [
308
+ {
309
+ colorbar: {
310
+ outlinewidth: 0,
311
+ ticks: "",
312
+ },
313
+ colorscale: [
314
+ [0.0, "#0d0887"],
315
+ [0.1111111111111111, "#46039f"],
316
+ [0.2222222222222222, "#7201a8"],
317
+ [0.3333333333333333, "#9c179e"],
318
+ [0.4444444444444444, "#bd3786"],
319
+ [0.5555555555555556, "#d8576b"],
320
+ [0.6666666666666666, "#ed7953"],
321
+ [0.7777777777777778, "#fb9f3a"],
322
+ [0.8888888888888888, "#fdca26"],
323
+ [1.0, "#f0f921"],
324
+ ],
325
+ type: "heatmap",
326
+ },
327
+ ],
328
+ histogram2dcontour: [
329
+ {
330
+ colorbar: {
331
+ outlinewidth: 0,
332
+ ticks: "",
333
+ },
334
+ colorscale: [
335
+ [0.0, "#0d0887"],
336
+ [0.1111111111111111, "#46039f"],
337
+ [0.2222222222222222, "#7201a8"],
338
+ [0.3333333333333333, "#9c179e"],
339
+ [0.4444444444444444, "#bd3786"],
340
+ [0.5555555555555556, "#d8576b"],
341
+ [0.6666666666666666, "#ed7953"],
342
+ [0.7777777777777778, "#fb9f3a"],
343
+ [0.8888888888888888, "#fdca26"],
344
+ [1.0, "#f0f921"],
345
+ ],
346
+ type: "histogram2dcontour",
347
+ },
348
+ ],
349
+ histogram2d: [
350
+ {
351
+ colorbar: {
352
+ outlinewidth: 0,
353
+ ticks: "",
354
+ },
355
+ colorscale: [
356
+ [0.0, "#0d0887"],
357
+ [0.1111111111111111, "#46039f"],
358
+ [0.2222222222222222, "#7201a8"],
359
+ [0.3333333333333333, "#9c179e"],
360
+ [0.4444444444444444, "#bd3786"],
361
+ [0.5555555555555556, "#d8576b"],
362
+ [0.6666666666666666, "#ed7953"],
363
+ [0.7777777777777778, "#fb9f3a"],
364
+ [0.8888888888888888, "#fdca26"],
365
+ [1.0, "#f0f921"],
366
+ ],
367
+ type: "histogram2d",
368
+ },
369
+ ],
370
+ histogram: [
371
+ {
372
+ marker: {
373
+ pattern: {
374
+ fillmode: "overlay",
375
+ size: 10,
376
+ solidity: 0.2,
377
+ },
378
+ },
379
+ type: "histogram",
380
+ },
381
+ ],
382
+ mesh3d: [
383
+ {
384
+ colorbar: {
385
+ outlinewidth: 0,
386
+ ticks: "",
387
+ },
388
+ type: "mesh3d",
389
+ },
390
+ ],
391
+ parcoords: [
392
+ {
393
+ line: {
394
+ colorbar: {
395
+ outlinewidth: 0,
396
+ ticks: "",
397
+ },
398
+ },
399
+ type: "parcoords",
400
+ },
401
+ ],
402
+ pie: [
403
+ {
404
+ automargin: true,
405
+ type: "pie",
406
+ },
407
+ ],
408
+ scatter3d: [
409
+ {
410
+ line: {
411
+ colorbar: {
412
+ outlinewidth: 0,
413
+ ticks: "",
414
+ },
415
+ },
416
+ marker: {
417
+ colorbar: {
418
+ outlinewidth: 0,
419
+ ticks: "",
420
+ },
421
+ },
422
+ type: "scatter3d",
423
+ },
424
+ ],
425
+ scattercarpet: [
426
+ {
427
+ marker: {
428
+ colorbar: {
429
+ outlinewidth: 0,
430
+ ticks: "",
431
+ },
432
+ },
433
+ type: "scattercarpet",
434
+ },
435
+ ],
436
+ scattergeo: [
437
+ {
438
+ marker: {
439
+ colorbar: {
440
+ outlinewidth: 0,
441
+ ticks: "",
442
+ },
443
+ },
444
+ type: "scattergeo",
445
+ },
446
+ ],
447
+ scattergl: [
448
+ {
449
+ marker: {
450
+ colorbar: {
451
+ outlinewidth: 0,
452
+ ticks: "",
453
+ },
454
+ },
455
+ type: "scattergl",
456
+ },
457
+ ],
458
+ scattermapbox: [
459
+ {
460
+ marker: {
461
+ colorbar: {
462
+ outlinewidth: 0,
463
+ ticks: "",
464
+ },
465
+ },
466
+ type: "scattermapbox",
467
+ },
468
+ ],
469
+ scatterpolargl: [
470
+ {
471
+ marker: {
472
+ colorbar: {
473
+ outlinewidth: 0,
474
+ ticks: "",
475
+ },
476
+ },
477
+ type: "scatterpolargl",
478
+ },
479
+ ],
480
+ scatterpolar: [
481
+ {
482
+ marker: {
483
+ colorbar: {
484
+ outlinewidth: 0,
485
+ ticks: "",
486
+ },
487
+ },
488
+ type: "scatterpolar",
489
+ },
490
+ ],
491
+ scatter: [
492
+ {
493
+ fillpattern: {
494
+ fillmode: "overlay",
495
+ size: 10,
496
+ solidity: 0.2,
497
+ },
498
+ type: "scatter",
499
+ },
500
+ ],
501
+ scatterternary: [
502
+ {
503
+ marker: {
504
+ colorbar: {
505
+ outlinewidth: 0,
506
+ ticks: "",
507
+ },
508
+ },
509
+ type: "scatterternary",
510
+ },
511
+ ],
512
+ surface: [
513
+ {
514
+ colorbar: {
515
+ outlinewidth: 0,
516
+ ticks: "",
517
+ },
518
+ colorscale: [
519
+ [0.0, "#0d0887"],
520
+ [0.1111111111111111, "#46039f"],
521
+ [0.2222222222222222, "#7201a8"],
522
+ [0.3333333333333333, "#9c179e"],
523
+ [0.4444444444444444, "#bd3786"],
524
+ [0.5555555555555556, "#d8576b"],
525
+ [0.6666666666666666, "#ed7953"],
526
+ [0.7777777777777778, "#fb9f3a"],
527
+ [0.8888888888888888, "#fdca26"],
528
+ [1.0, "#f0f921"],
529
+ ],
530
+ type: "surface",
531
+ },
532
+ ],
533
+ table: [
534
+ {
535
+ cells: {
536
+ fill: {
537
+ color: "#EBF0F8",
538
+ },
539
+ line: {
540
+ color: "white",
541
+ },
542
+ },
543
+ header: {
544
+ fill: {
545
+ color: "#C8D4E3",
546
+ },
547
+ line: {
548
+ color: "white",
549
+ },
550
+ },
551
+ type: "table",
552
+ },
553
+ ],
554
+ candlestick: [
555
+ {
556
+ "decreasing": {
557
+ "fillcolor": "#e4003a",
558
+ "line": {
559
+ "color": "#e4003a"
560
+ }
561
+ },
562
+ "increasing": {
563
+ "fillcolor": "#00ACFF",
564
+ "line": {
565
+ "color": "#00ACFF"
566
+ }
567
+ },
568
+ "type": "candlestick"
569
+ }
570
+ ],
571
+ },
572
+ layout: {
573
+ annotationdefaults: {
574
+ arrowcolor: "#2a3f5f",
575
+ arrowhead: 0,
576
+ arrowwidth: 1,
577
+ showarrow: false,
578
+ },
579
+ autotypenumbers: "strict",
580
+ coloraxis: {
581
+ colorbar: {
582
+ outlinewidth: 0,
583
+ ticks: "",
584
+ },
585
+ },
586
+ colorscale: {
587
+ diverging: [
588
+ [0, "#8e0152"],
589
+ [0.1, "#c51b7d"],
590
+ [0.2, "#de77ae"],
591
+ [0.3, "#f1b6da"],
592
+ [0.4, "#fde0ef"],
593
+ [0.5, "#f7f7f7"],
594
+ [0.6, "#e6f5d0"],
595
+ [0.7, "#b8e186"],
596
+ [0.8, "#7fbc41"],
597
+ [0.9, "#4d9221"],
598
+ [1, "#276419"],
599
+ ],
600
+ sequential: [
601
+ [0.0, "#0d0887"],
602
+ [0.1111111111111111, "#46039f"],
603
+ [0.2222222222222222, "#7201a8"],
604
+ [0.3333333333333333, "#9c179e"],
605
+ [0.4444444444444444, "#bd3786"],
606
+ [0.5555555555555556, "#d8576b"],
607
+ [0.6666666666666666, "#ed7953"],
608
+ [0.7777777777777778, "#fb9f3a"],
609
+ [0.8888888888888888, "#fdca26"],
610
+ [1.0, "#f0f921"],
611
+ ],
612
+ sequentialminus: [
613
+ [0.0, "#0d0887"],
614
+ [0.1111111111111111, "#46039f"],
615
+ [0.2222222222222222, "#7201a8"],
616
+ [0.3333333333333333, "#9c179e"],
617
+ [0.4444444444444444, "#bd3786"],
618
+ [0.5555555555555556, "#d8576b"],
619
+ [0.6666666666666666, "#ed7953"],
620
+ [0.7777777777777778, "#fb9f3a"],
621
+ [0.8888888888888888, "#fdca26"],
622
+ [1.0, "#f0f921"],
623
+ ],
624
+ },
625
+ colorway: [
626
+ "#1f77b4",
627
+ "#ff7f0e",
628
+ "#2ca02c",
629
+ "#d62728",
630
+ "#9467bd",
631
+ "#8c564b",
632
+ "#e377c2",
633
+ "#bcbd22",
634
+ "#17becf",
635
+ "#aec7e8",
636
+ "#ffbb78",
637
+ "#ff9896",
638
+ "#c5b0d5",
639
+ "#f7b6d2",
640
+ "#dbdb8d",
641
+ "#9edae5"
642
+ ],
643
+ font: {
644
+ family: "Arial, Helvetica, sans-serif",
645
+ size: 16,
646
+ },
647
+ geo: {
648
+ bgcolor: "white",
649
+ lakecolor: "white",
650
+ landcolor: "white",
651
+ showlakes: true,
652
+ showland: true,
653
+ subunitcolor: "#C8D4E3",
654
+ },
655
+ hoverlabel: {
656
+ align: "left",
657
+ font: {
658
+ family: "Arial, Helvetica, sans-serif",
659
+ size: 14,
660
+ },
661
+ },
662
+ hovermode: "x",
663
+ mapbox: {
664
+ style: "light",
665
+ },
666
+ paper_bgcolor: "#FFFFFF",
667
+ plot_bgcolor: "#FFFFFF",
668
+ polar: {
669
+ angularaxis: {
670
+ gridcolor: "#EBF0F8",
671
+ linecolor: "#EBF0F8",
672
+ ticks: "",
673
+ },
674
+ bgcolor: "white",
675
+ radialaxis: {
676
+ gridcolor: "#EBF0F8",
677
+ linecolor: "#EBF0F8",
678
+ ticks: "",
679
+ },
680
+ },
681
+ scene: {
682
+ xaxis: {
683
+ backgroundcolor: "white",
684
+ gridcolor: "#DFE8F3",
685
+ gridwidth: 2,
686
+ linecolor: "#EBF0F8",
687
+ showbackground: true,
688
+ ticks: "",
689
+ zerolinecolor: "#EBF0F8",
690
+ },
691
+ yaxis: {
692
+ backgroundcolor: "white",
693
+ gridcolor: "#DFE8F3",
694
+ gridwidth: 2,
695
+ linecolor: "#EBF0F8",
696
+ showbackground: true,
697
+ ticks: "",
698
+ zerolinecolor: "#EBF0F8",
699
+ },
700
+ zaxis: {
701
+ backgroundcolor: "white",
702
+ gridcolor: "#DFE8F3",
703
+ gridwidth: 2,
704
+ linecolor: "#EBF0F8",
705
+ showbackground: true,
706
+ ticks: "",
707
+ zerolinecolor: "#EBF0F8",
708
+ },
709
+ },
710
+ shapedefaults: {
711
+ line: {
712
+ color: "#2a3f5f",
713
+ },
714
+ },
715
+ ternary: {
716
+ aaxis: {
717
+ gridcolor: "#DFE8F3",
718
+ linecolor: "#A2B1C6",
719
+ ticks: "",
720
+ },
721
+ baxis: {
722
+ gridcolor: "#DFE8F3",
723
+ linecolor: "#A2B1C6",
724
+ ticks: "",
725
+ },
726
+ bgcolor: "white",
727
+ caxis: {
728
+ gridcolor: "#DFE8F3",
729
+ linecolor: "#A2B1C6",
730
+ ticks: "",
731
+ },
732
+ },
733
+ title: {
734
+ x: 0.05,
735
+ },
736
+ xaxis: {
737
+ automargin: true,
738
+ autorange: true,
739
+ rangeslider: {
740
+ visible: false
741
+ },
742
+ showgrid: true,
743
+ showline: true,
744
+ tickfont: {
745
+ family: "Arial, Helvetica, sans-serif",
746
+ size: 14,
747
+ },
748
+ zeroline: false,
749
+ tick0: 1,
750
+ title: {
751
+ standoff: 20,
752
+ font: {
753
+ family: "Arial, Helvetica, sans-serif",
754
+ size: 16,
755
+ },
756
+ },
757
+ gridcolor: "#283442",
758
+ linecolor: "#A9A9A9",
759
+ mirror: true,
760
+ ticks: "outside"
761
+ },
762
+ yaxis: {
763
+ anchor: "x",
764
+ automargin: true,
765
+ fixedrange: false,
766
+ zeroline: false,
767
+ showgrid: true,
768
+ showline: true,
769
+ side: "right",
770
+ tick0: 0.5,
771
+ tickfont: {
772
+ family: "Arial, Helvetica, sans-serif",
773
+ size: 14,
774
+ },
775
+ title: {
776
+ standoff: 20,
777
+ font: {
778
+ family: "Arial, Helvetica, sans-serif",
779
+ size: 16,
780
+ },
781
+ },
782
+ gridcolor: "rgba(128, 128, 128, 0.33)",
783
+ linecolor: "#A9A9A9",
784
+ mirror: true,
785
+ ticks: "outside"
786
+ },
787
+ dragmode: "pan",
788
+ legend: {
789
+ bgcolor: "rgba(255, 255, 255, 0)",
790
+ x: 1,
791
+ xanchor: "right",
792
+ y: 1.02,
793
+ yanchor: "bottom",
794
+ font: {
795
+ family: "Arial, Helvetica, sans-serif",
796
+ size: 14,
797
+ },
798
+ },
799
+ },
800
+ };
frontend-components/plotly/src/components/Dialogs/AlertDialog.tsx ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import CommonDialog from "../Dialogs/CommonDialog";
2
+
3
+ export default function AlertDialog({
4
+ title,
5
+ content,
6
+ open,
7
+ close,
8
+ }: {
9
+ title: string;
10
+ content: string;
11
+ open: boolean;
12
+ close: () => void;
13
+ }) {
14
+ return (
15
+ <CommonDialog title={title} description="" open={open} close={close}>
16
+ <div
17
+ id="popup_title"
18
+ className="popup_content"
19
+ style={{ padding: "0px 2px 2px 5px", marginTop: 5 }}
20
+ >
21
+ <div style={{ display: "flex", flexDirection: "column", gap: 0 }}>
22
+ <div>
23
+ <label htmlFor="title_text">{content}</label>
24
+ </div>
25
+ </div>
26
+ <div style={{ float: "right", marginTop: 20 }}>
27
+ <button
28
+ type="button"
29
+ className="_btn"
30
+ style={{
31
+ padding: "8px 16px",
32
+ width: "100%",
33
+ }}
34
+ onClick={close}
35
+ >
36
+ Close
37
+ </button>
38
+ </div>
39
+ </div>
40
+ </CommonDialog>
41
+ );
42
+ }
frontend-components/plotly/src/components/Dialogs/CommonDialog.tsx ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
2
+ import CloseIcon from "../Icons/Close";
3
+ import { ReactNode } from "react";
4
+
5
+ export const styleDialog = {
6
+ margin: "2px 0px 2px 10px",
7
+ padding: "5px 2px 2px 5px",
8
+ };
9
+
10
+ export default function CommonDialog({
11
+ open,
12
+ close,
13
+ title,
14
+ description,
15
+ children,
16
+ }: {
17
+ open: boolean;
18
+ close: () => void;
19
+ title: string;
20
+ description: string;
21
+ children: ReactNode;
22
+ }) {
23
+ return (
24
+ <DialogPrimitive.Root open={open} onOpenChange={close}>
25
+ <DialogPrimitive.Overlay onClick={close} className="_modal-overlay" />
26
+ <DialogPrimitive.Content className="_modal">
27
+ <DialogPrimitive.Title className="_modal-title">
28
+ {title}
29
+ </DialogPrimitive.Title>
30
+ <DialogPrimitive.Description className="_modal_description">
31
+ {description}
32
+ </DialogPrimitive.Description>
33
+ <DialogPrimitive.Close>
34
+ <CloseIcon />
35
+ </DialogPrimitive.Close>
36
+ {children}
37
+ <DialogPrimitive.Close className="_modal-close" onClick={close}>
38
+ <CloseIcon className="w-6 h-6" />
39
+ </DialogPrimitive.Close>
40
+ </DialogPrimitive.Content>
41
+ </DialogPrimitive.Root>
42
+ );
43
+ }
frontend-components/plotly/src/components/Dialogs/OverlayChartDialog.tsx ADDED
@@ -0,0 +1,651 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState } from "react";
2
+ import CommonDialog, { styleDialog } from "../Dialogs/CommonDialog";
3
+
4
+ const reader = new FileReader();
5
+
6
+ const layout_defaults = {
7
+ overlaying: "y",
8
+ side: "left",
9
+ tickfont: { size: 12 },
10
+ tickpadding: 5,
11
+ showgrid: false,
12
+ showline: false,
13
+ showticklabels: true,
14
+ showlegend: true,
15
+ zeroline: false,
16
+ anchor: "x",
17
+ type: "linear",
18
+ autorange: true,
19
+ };
20
+
21
+ export default function OverlayChartDialog({
22
+ open,
23
+ close,
24
+ setLoading,
25
+ addOverlay,
26
+ plotlyData,
27
+ }: {
28
+ open: boolean;
29
+ close: () => void;
30
+ setLoading: (loading: boolean) => void;
31
+ addOverlay: (data: any) => void;
32
+ plotlyData: any;
33
+ }) {
34
+ const [traceType, setTraceType] = useState("scatter");
35
+ const [traceColor, setTraceColor] = useState("#FFDD00");
36
+ const [increasingColor, setIncreasingColor] = useState("#00ACFF");
37
+ const [decreasingColor, setDecreasingColor] = useState("#FF0000");
38
+ const [traceName, setTraceName] = useState("");
39
+ const [csvData, setCsvData] = useState<any[]>([]);
40
+ const [csvColumns, setCsvColumns] = useState<string[]>([]);
41
+ const [yaxisOptions, setYaxisOptions] = useState<any>({});
42
+ const optionIds = ["x", "open", "high", "low", "close"];
43
+
44
+ const traceTypes: any = {
45
+ scatter: "Scatter (Line)",
46
+ candlestick: "Candlestick",
47
+ bar: "Bar",
48
+ };
49
+
50
+ const [options, setOptions] = useState<any>({});
51
+
52
+ function onClose() {
53
+ close();
54
+ setTraceType("scatter");
55
+ setTraceName("");
56
+ setCsvData([]);
57
+ setCsvColumns([]);
58
+ setOptions({});
59
+ }
60
+
61
+ function onSubmit() {
62
+ if (csvData.length === 0) {
63
+ document.getElementById("csv_file")?.focus();
64
+ document
65
+ .getElementById("csv_file")
66
+ ?.style.setProperty("border", "1px solid red");
67
+ document.getElementById("csv_file_warning")!.style.display = "block";
68
+ return;
69
+ }
70
+ const newPlotydata = CSVonSubmit({
71
+ csvData: csvData,
72
+ plotlyData: plotlyData,
73
+ yaxisOptions: yaxisOptions,
74
+ traceType: traceType,
75
+ traceColor: traceColor,
76
+ traceName: traceName,
77
+ options: options,
78
+ increasingColor: increasingColor,
79
+ decreasingColor: decreasingColor,
80
+ });
81
+ addOverlay(newPlotydata);
82
+ onClose();
83
+ }
84
+
85
+ return (
86
+ <CommonDialog
87
+ title="Overlay Chart"
88
+ description="Upload a CSV file to overlay a chart on the main chart."
89
+ open={open}
90
+ close={close}
91
+ >
92
+ <div id="popup_csv" className="popup_content">
93
+ <div>
94
+ <label htmlFor="csv_file">
95
+ <b>CSV file:</b>
96
+ <div
97
+ id="csv_file_warning"
98
+ className="popup_warning"
99
+ style={{ marginLeft: "80px", marginBottom: "10px" }}
100
+ >
101
+ CSV file is required.
102
+ </div>
103
+ </label>
104
+ <input
105
+ onChange={(e) => {
106
+ if (!e.target.files) {
107
+ return;
108
+ } else if (e.target.files[0].type !== "text/csv") {
109
+ document.getElementById("csv_file")?.focus();
110
+ document
111
+ .getElementById("csv_file")
112
+ ?.style.setProperty("border", "1px solid red");
113
+ document.getElementById("csv_file_warning")!.style.display =
114
+ "block";
115
+ return;
116
+ }
117
+
118
+ if (csvColumns.length > 0) {
119
+ setCsvColumns([]);
120
+ setOptions({});
121
+ setTraceType("scatter");
122
+ }
123
+
124
+ reader.onload = (filebytes) => {
125
+ if (
126
+ !filebytes.target?.result ||
127
+ typeof filebytes.target.result !== "string"
128
+ ) {
129
+ return;
130
+ }
131
+ const lines = filebytes.target.result
132
+ .split("\n")
133
+ .map((x) => x.replace(/\r/g, ""));
134
+
135
+ const headers = lines[0].split(",");
136
+ const headers_lower = headers.map((x) =>
137
+ x.trim().toLowerCase(),
138
+ );
139
+
140
+ const updateOptions: { [key: string]: any } = {};
141
+
142
+ if (headers.length > 1) {
143
+ updateOptions.x = headers[0];
144
+ updateOptions.y = headers[1];
145
+ }
146
+
147
+ for (let i = 0; i < optionIds.length; i++) {
148
+ if (headers_lower.includes(optionIds[i])) {
149
+ updateOptions[optionIds[i]] =
150
+ headers[headers_lower.indexOf(optionIds[i])];
151
+ } else if (
152
+ optionIds[i] === "x" &&
153
+ headers_lower.includes("date")
154
+ ) {
155
+ updateOptions[optionIds[i]] =
156
+ headers[headers_lower.indexOf("date")];
157
+ }
158
+ }
159
+
160
+ const candle_cols = ["open", "high", "low", "close"];
161
+ const candle_cols_present = candle_cols.every((x) =>
162
+ headers_lower.includes(x),
163
+ );
164
+ if (candle_cols_present) {
165
+ setTraceType("candlestick");
166
+ } else if (headers_lower.length >= 5) {
167
+ candle_cols.forEach((x) => {
168
+ updateOptions[x] = headers[candle_cols.indexOf(x) + 1];
169
+ });
170
+ }
171
+
172
+ if (headers_lower.includes("close")) {
173
+ setOptions({
174
+ ...options,
175
+ y: headers[headers_lower.indexOf("close")],
176
+ });
177
+ updateOptions.y = headers[headers_lower.indexOf("close")];
178
+ }
179
+
180
+ const data = [];
181
+
182
+ for (let i = 1; i < lines.length; i++) {
183
+ const obj = {};
184
+ const currentline = lines[i].split(",");
185
+ for (let j = 0; j < headers.length; j++) {
186
+ //@ts-ignore
187
+ obj[headers[j]] = currentline[j];
188
+ }
189
+ data.push(obj);
190
+ }
191
+
192
+ //@ts-ignore
193
+ let filename = e.target.files[0].name.split(".")[0];
194
+
195
+ try {
196
+ if (filename.includes("_")) {
197
+ const name_parts = filename
198
+ .replace(/_{2,}/g, "_")
199
+ .split("_");
200
+ const date_regex = new RegExp("^[0-9]{8}$");
201
+
202
+ if (name_parts.length > 2) {
203
+ // we check if the first 2 parts are date and time
204
+ if (date_regex.test(name_parts[0])) {
205
+ name_parts.splice(0, 2);
206
+ }
207
+ // we check if the last 2 parts are date and time
208
+ else if (
209
+ date_regex.test(name_parts[name_parts.length - 2])
210
+ ) {
211
+ name_parts.splice(name_parts.length - 2, 2);
212
+ }
213
+ filename = name_parts.join("_").replace(/openbb_/g, "");
214
+ }
215
+ }
216
+ } catch (e) {
217
+ console.log(e);
218
+ }
219
+
220
+ setTraceName(filename);
221
+ setOptions(updateOptions);
222
+ setCsvColumns(headers);
223
+ setCsvData(data);
224
+ };
225
+ reader.readAsText(e.target.files[0]);
226
+ }}
227
+ type="file"
228
+ id="csv_file"
229
+ accept=".csv"
230
+ style={{ marginLeft: 10 }}
231
+ />
232
+ </div>
233
+ <div style={{ marginTop: 15 }}>
234
+ <label htmlFor="csv_trace_type">
235
+ <b>Display data type:</b>
236
+ </label>
237
+ <select
238
+ onChange={(e) => {
239
+ setTraceType(e.target.value);
240
+ }}
241
+ id="csv_trace_type"
242
+ style={styleDialog}
243
+ defaultValue={traceTypes[traceType]}
244
+ >
245
+ {traceType && (
246
+ <option key={traceType} value={traceType}>
247
+ {traceTypes[traceType]}
248
+ </option>
249
+ )}
250
+ {Object.keys(traceTypes).map(
251
+ (x) =>
252
+ traceType !== x && (
253
+ <option key={x} value={x}>
254
+ {traceTypes[x]}
255
+ </option>
256
+ ),
257
+ )}
258
+ </select>
259
+ </div>
260
+ <div style={{ marginTop: 12 }}>
261
+ <label htmlFor="csv_name">
262
+ <b>Trace Name:</b>
263
+ </label>
264
+ <textarea
265
+ id="csv_name"
266
+ value={traceName}
267
+ onChange={(e) => {
268
+ setTraceName(e.target.value);
269
+ }}
270
+ style={{
271
+ padding: "5px 2px 2px 5px",
272
+ width: "100%",
273
+ maxWidth: "100%",
274
+ maxHeight: 200,
275
+ marginTop: 2,
276
+ }}
277
+ rows={2}
278
+ cols={20}
279
+ placeholder="Enter a name to give this trace"
280
+ />
281
+ </div>
282
+ {csvColumns.length > 0 && (
283
+ <>
284
+ {["scatter", "bar"].includes(traceType) && (
285
+ <div
286
+ style={{ marginTop: 15, marginBottom: 10 }}
287
+ id="csv_columns"
288
+ className="csv_column_container"
289
+ >
290
+ {["x", "y"].map((key) => (
291
+ <div
292
+ key={key}
293
+ style={{
294
+ marginTop: 10,
295
+ display: "flex",
296
+ alignItems: "center",
297
+ justifyContent: "space-between",
298
+ }}
299
+ >
300
+ <label htmlFor={`csv_${key}`} style={{ width: "100px" }}>
301
+ {key.toUpperCase()} Axis
302
+ </label>
303
+ <select
304
+ onChange={(e) => {
305
+ setOptions({
306
+ ...options,
307
+ [key]: e.target.value,
308
+ });
309
+ }}
310
+ id={`csv_${key}`}
311
+ style={{ width: "100%" }}
312
+ defaultValue={options[key]}
313
+ >
314
+ {csvColumns.map((column) => (
315
+ <option key={column} value={column}>
316
+ {column}
317
+ </option>
318
+ ))}
319
+ </select>
320
+ </div>
321
+ ))}
322
+ </div>
323
+ )}
324
+ {traceType === "candlestick" && (
325
+ <div
326
+ id="csv_columns"
327
+ className="csv_column_container"
328
+ style={{ marginTop: 15 }}
329
+ >
330
+ {["x", "open", "high", "low", "close"].map((key) => (
331
+ <div
332
+ key={key}
333
+ style={{
334
+ marginTop: 10,
335
+ display: "flex",
336
+ alignItems: "center",
337
+ justifyContent: "space-between",
338
+ }}
339
+ >
340
+ <label htmlFor={`csv_${key}`} style={{ width: "100px" }}>
341
+ {key.charAt(0).toUpperCase() + key.slice(1)}
342
+ </label>
343
+ <select
344
+ onChange={(e) => {
345
+ setOptions({
346
+ ...options,
347
+ [key]: e.target.value,
348
+ });
349
+ }}
350
+ id={`csv_${key}`}
351
+ style={{ width: "100%" }}
352
+ defaultValue={options[key]}
353
+ >
354
+ {csvColumns.map((column) => (
355
+ <option key={column} value={column}>
356
+ {column}
357
+ </option>
358
+ ))}
359
+ </select>
360
+ </div>
361
+ ))}
362
+ </div>
363
+ )}
364
+ <div style={{ marginTop: 20 }} id="csv_colors">
365
+ {["scatter", "bar"].includes(traceType) && (
366
+ <div>
367
+ <label htmlFor="csv_color">{`${traceType
368
+ .charAt(0)
369
+ .toUpperCase()}${traceType.slice(1)} color`}</label>
370
+ <input
371
+ type="color"
372
+ id="csv_color"
373
+ defaultValue="#FFDD00"
374
+ style={{ margin: "2px 2px 2px 10px" }}
375
+ onChange={(e) => {
376
+ console.log(e.target.value);
377
+ setTraceColor(e.target.value);
378
+ }}
379
+ />
380
+ </div>
381
+ )}
382
+ {traceType === "candlestick" && (
383
+ <>
384
+ <label htmlFor="csv_increasing">Increasing color</label>
385
+ <input
386
+ type="color"
387
+ id="csv_increasing"
388
+ defaultValue="#00ACFF"
389
+ style={{ margin: "2px 0px 2px 10px" }}
390
+ onChange={(e) => {
391
+ setIncreasingColor(e.target.value);
392
+ }}
393
+ />
394
+ <label htmlFor="csv_decreasing" style={{ marginLeft: 15 }}>
395
+ Decreasing color
396
+ </label>
397
+ <input
398
+ style={{ margin: "2px 0px 2px 10px" }}
399
+ type="color"
400
+ id="csv_decreasing"
401
+ defaultValue="#FF0000"
402
+ onChange={(e) => {
403
+ setDecreasingColor(e.target.value);
404
+ }}
405
+ />
406
+ </>
407
+ )}
408
+ </div>
409
+ <div style={{ marginTop: 20 }} id="csv_plot_yaxis_options">
410
+ {traceType !== "candlestick" && (
411
+ <>
412
+ <input
413
+ type="checkbox"
414
+ id="csv_percent_change"
415
+ name="csv_plot_yaxis_check"
416
+ style={{ marginBottom: 2 }}
417
+ onChange={(e) => {
418
+ setYaxisOptions({
419
+ ...yaxisOptions,
420
+ percentChange: e.target.checked,
421
+ sameYaxis: false,
422
+ });
423
+ }}
424
+ checked={
425
+ !yaxisOptions.sameYaxis && yaxisOptions.percentChange
426
+ }
427
+ />
428
+ <label htmlFor="csv_percent_change" style={{ marginLeft: 5 }}>
429
+ Plot as percent change from first value
430
+ </label>
431
+ <br />
432
+ </>
433
+ )}
434
+ <input
435
+ style={{ marginTop: 2 }}
436
+ type="checkbox"
437
+ id="csv_same_yaxis"
438
+ name="csv_plot_yaxis_check"
439
+ onChange={(e) => {
440
+ setYaxisOptions({
441
+ ...yaxisOptions,
442
+ sameYaxis: e.target.checked,
443
+ percentChange: false,
444
+ });
445
+ }}
446
+ checked={!yaxisOptions.percentChange && yaxisOptions.sameYaxis}
447
+ />
448
+ <label htmlFor="csv_same_yaxis" style={{ marginLeft: 5 }}>
449
+ Share Y-axis
450
+ </label>
451
+
452
+ {traceType === "bar" && (
453
+ <div style={{ marginTop: 2 }} id="csv_bar_orientation">
454
+ <input
455
+ type="checkbox"
456
+ id="csv_bar_horizontal"
457
+ onChange={(e) => {
458
+ setOptions({
459
+ ...options,
460
+ orientation: e.target.checked ? "h" : "v",
461
+ });
462
+ }}
463
+ />
464
+ <label htmlFor="csv_bar_horizontal" style={{ marginLeft: 5 }}>
465
+ Plot horizontally
466
+ </label>
467
+ </div>
468
+ )}
469
+ </div>
470
+ </>
471
+ )}
472
+
473
+ <br />
474
+ <div style={{ float: "right", marginTop: 20 }}>
475
+ <button className="_btn-tertiary" id="csv_cancel" onClick={onClose}>
476
+ Cancel
477
+ </button>
478
+ <button className="_btn" id="csv_submit" onClick={onSubmit}>
479
+ Submit
480
+ </button>
481
+ </div>
482
+ </div>
483
+ </CommonDialog>
484
+ );
485
+ }
486
+
487
+ export function CSVonSubmit({
488
+ csvData,
489
+ plotlyData,
490
+ yaxisOptions,
491
+ traceType,
492
+ traceColor,
493
+ traceName,
494
+ options,
495
+ increasingColor,
496
+ decreasingColor,
497
+ }: {
498
+ csvData: any[];
499
+ plotlyData: any;
500
+ yaxisOptions: any;
501
+ traceType: string;
502
+ traceColor: string;
503
+ traceName: string;
504
+ options: any;
505
+ increasingColor: string;
506
+ decreasingColor: string;
507
+ }) {
508
+ console.log("options", options);
509
+ const main_trace = plotlyData.data[0] || {};
510
+ if (main_trace.xaxis === undefined) {
511
+ main_trace.xaxis = "x";
512
+ }
513
+ if (main_trace.yaxis === undefined) {
514
+ main_trace.yaxis = "y";
515
+ }
516
+ let yaxis_id = main_trace.yaxis;
517
+ let yaxis: string;
518
+
519
+ const left_yaxis_ticks = Object.keys(plotlyData.layout)
520
+ .filter((k) => k.startsWith("yaxis"))
521
+ .map((k) => plotlyData.layout[k])
522
+ .filter(
523
+ (yaxis) =>
524
+ yaxis.side === "left" &&
525
+ (yaxis.overlaying === "y" ||
526
+ (yaxis.fixedrange !== undefined && yaxis.fixedrange === true)),
527
+ ).length;
528
+
529
+ const ticksuffix = left_yaxis_ticks > 0 ? " " : "";
530
+
531
+ if (yaxisOptions.sameYaxis !== true) {
532
+ const yaxes = Object.keys(plotlyData.layout)
533
+ .filter((k) => k.startsWith("yaxis"))
534
+ .map((k) => plotlyData.layout[k]);
535
+
536
+ yaxis = `y${yaxes.length + 1}`;
537
+ yaxis_id = `yaxis${yaxes.length + 1}`;
538
+ plotlyData.layout[yaxis_id] = {
539
+ ...layout_defaults,
540
+ title: {
541
+ text: traceName,
542
+ font: {
543
+ size: 14,
544
+ },
545
+ standoff: 0,
546
+ },
547
+ ticksuffix: ticksuffix,
548
+ layer: "below traces",
549
+ };
550
+ } else {
551
+ // Plot on the same yaxis
552
+ yaxis = main_trace.yaxis.replace("yaxis", "y");
553
+ }
554
+
555
+ const traceBase: any = {
556
+ type: traceType,
557
+ name: traceName,
558
+ showlegend: true,
559
+ yaxis: yaxis,
560
+ };
561
+
562
+ let trace: any = {};
563
+
564
+ if (["scatter", "bar"].includes(traceType)) {
565
+ if (!csvData || csvData.length === 0) return plotlyData;
566
+ const non_null = csvData.findIndex(
567
+ (x: any) => x[options.y] !== null && x[options.y] !== 0,
568
+ );
569
+
570
+ if (non_null === -1) {
571
+ return plotlyData;
572
+ }
573
+
574
+ const scatter_data: { [key: string]: any[] } = {
575
+ x: [],
576
+ y: [],
577
+ customdata: [],
578
+ };
579
+
580
+ csvData.forEach((row: any) => {
581
+ let y = row[options.y];
582
+ scatter_data.customdata.push(y);
583
+ if (
584
+ yaxisOptions.percentChange &&
585
+ (traceType === "scatter" || traceType === "line")
586
+ ) {
587
+ y =
588
+ (row[options.y] - csvData[non_null][options.y]) /
589
+ csvData[non_null][options.y];
590
+ }
591
+ scatter_data.x.push(row[options.x]);
592
+ scatter_data.y.push(y);
593
+ });
594
+
595
+ trace = {
596
+ ...traceBase,
597
+ x: scatter_data.x,
598
+ y: scatter_data.y,
599
+ customdata: scatter_data.customdata,
600
+ hovertemplate: "%{customdata:.2f}",
601
+ connectgaps: true,
602
+ marker: { color: traceColor },
603
+ };
604
+
605
+ if (traceType === "bar") {
606
+ trace.orientation = options.orientation;
607
+ trace.marker.opacity = 0.7;
608
+ trace.connectgaps = undefined;
609
+ trace.hovertemplate = undefined;
610
+ trace.customdata = undefined;
611
+ }
612
+ } else if (traceType === "candlestick") {
613
+ const candlestick_data: { [key: string]: any[] } = {
614
+ x: [],
615
+ open: [],
616
+ high: [],
617
+ low: [],
618
+ close: [],
619
+ };
620
+
621
+ csvData.forEach((row: any) => {
622
+ candlestick_data.x.push(row[options.x]);
623
+ candlestick_data.open.push(row[options.open]);
624
+ candlestick_data.high.push(row[options.high]);
625
+ candlestick_data.low.push(row[options.low]);
626
+ candlestick_data.close.push(row[options.close]);
627
+ });
628
+
629
+ trace = {
630
+ ...traceBase,
631
+ x: candlestick_data.x,
632
+ open: candlestick_data.open,
633
+ high: candlestick_data.high,
634
+ low: candlestick_data.low,
635
+ close: candlestick_data.close,
636
+ increasing: {
637
+ line: { color: increasingColor, width: 0.8 },
638
+ fillcolor: increasingColor,
639
+ },
640
+ decreasing: {
641
+ line: { color: decreasingColor, width: 0.8 },
642
+ fillcolor: decreasingColor,
643
+ },
644
+ };
645
+ }
646
+
647
+ return {
648
+ ...plotlyData,
649
+ data: [...plotlyData.data, trace],
650
+ };
651
+ }
frontend-components/plotly/src/components/Dialogs/TextChartDialog.tsx ADDED
@@ -0,0 +1,315 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import CommonDialog from "./CommonDialog";
2
+ import { useState, useEffect, useRef } from "react";
3
+
4
+ const style = {
5
+ padding: "5px 2px 2px 5px",
6
+ margin: "2px 0",
7
+ };
8
+
9
+ export default function TextChartDialog({
10
+ open,
11
+ close,
12
+ addAnnotation,
13
+ deleteAnnotation,
14
+ popupData,
15
+ }: {
16
+ plotlyData: any;
17
+ open: boolean;
18
+ close: () => void;
19
+ addAnnotation: (annotation: any) => void;
20
+ updateAnnotation?: (annotation: any) => void;
21
+ deleteAnnotation: (annotation: any) => void;
22
+ popupData: any | null;
23
+ }) {
24
+ // Prevent multiple renderings
25
+ const hasLoaded = useRef(false);
26
+
27
+ const defaultPopupData = {
28
+ text: "",
29
+ color: "#0088CC",
30
+ size: 18,
31
+ bordercolor: "#822661",
32
+ arrowcolor: "#822661",
33
+ bgcolor: "#000000",
34
+ arrowsize: 1,
35
+ arrowwidth: 2,
36
+ yanchor: "above",
37
+ };
38
+
39
+ // Use a single state object to hold all form data
40
+ const [formData, setFormData] = useState<any>(defaultPopupData);
41
+ const [editMode, setEditMode] = useState(false);
42
+
43
+ // Handle initialization when dialog opens
44
+ useEffect(() => {
45
+ if (open && popupData?.annotation && !hasLoaded.current) {
46
+ const annotation = popupData.annotation;
47
+
48
+ // Get properties from annotation for editing
49
+ let data = {
50
+ text: annotation.text || "",
51
+ color: annotation.font?.color || defaultPopupData.color,
52
+ size: annotation.font?.size || defaultPopupData.size,
53
+ bordercolor: annotation.bordercolor || defaultPopupData.bordercolor,
54
+ bgcolor: annotation.bgcolor || defaultPopupData.bgcolor,
55
+ arrowcolor: annotation.arrowcolor || defaultPopupData.arrowcolor,
56
+ arrowsize: annotation.arrowsize || defaultPopupData.arrowsize,
57
+ arrowwidth: annotation.arrowwidth || defaultPopupData.arrowwidth,
58
+ yanchor: "above",
59
+ };
60
+
61
+ // Determine position based on annotation coordinates
62
+ if (annotation.y !== undefined && annotation.ay !== undefined) {
63
+ data.yanchor = annotation.y < annotation.ay ? "above" : "below";
64
+ }
65
+
66
+ setFormData(data);
67
+ setEditMode(true);
68
+ hasLoaded.current = true;
69
+ } else if (!open) {
70
+ // Reset when dialog closes
71
+ setFormData(defaultPopupData);
72
+ setEditMode(false);
73
+ hasLoaded.current = false;
74
+ } else if (open && !popupData?.annotation) {
75
+ // Reset for new annotations
76
+ setFormData(defaultPopupData);
77
+ setEditMode(false);
78
+ }
79
+ }, [open, popupData]);
80
+
81
+ function onChange(e: any) {
82
+ const name = e.target.id.replace("addtext_", "");
83
+ let value = e.target.value;
84
+
85
+ // Convert numeric values
86
+ if (name === "size" || name === "arrowsize" || name === "arrowwidth") {
87
+ value = parseFloat(value);
88
+ }
89
+
90
+ setFormData((prev: any) => ({
91
+ ...prev,
92
+ [name]: value
93
+ }));
94
+ }
95
+
96
+ function onClose() {
97
+ close();
98
+ }
99
+
100
+ function onSubmit() {
101
+ if (formData.text) {
102
+ const dataToSubmit = { ...formData };
103
+
104
+ // Add the annotation reference for editing
105
+ if (editMode && popupData?.annotation) {
106
+ dataToSubmit.annotation = popupData.annotation;
107
+ }
108
+
109
+ addAnnotation(dataToSubmit);
110
+ close();
111
+ } else {
112
+ if (document.getElementById("popup_textarea_warning")) {
113
+ document.getElementById("popup_textarea_warning")!.style.display = "block";
114
+ }
115
+ if (document.getElementById("addtext_text")) {
116
+ document.getElementById("addtext_text")!.style.border = "1px solid red";
117
+ }
118
+ }
119
+ }
120
+
121
+ function onDelete() {
122
+ if (editMode && popupData) {
123
+ deleteAnnotation(popupData);
124
+ }
125
+ close();
126
+ }
127
+
128
+ return (
129
+ <CommonDialog
130
+ title={editMode ? "Edit Annotation" : "Add Text to Chart"}
131
+ description="Add or edit text annotations on the chart."
132
+ open={open}
133
+ close={onClose}
134
+ >
135
+ <div id="popup_title" className="popup_content">
136
+ <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
137
+ <div style={{ marginBottom: 20 }}>
138
+ <label htmlFor="popup_text">
139
+ <b>Text:</b>
140
+ <div id="popup_textarea_warning" className="popup_warning" style={{display: "none"}}>
141
+ Text is required
142
+ </div>
143
+ </label>
144
+ <textarea
145
+ id="addtext_text"
146
+ style={{
147
+ ...style,
148
+ width: "100%",
149
+ maxWidth: "100%",
150
+ maxHeight: "200px",
151
+ marginTop: "8px",
152
+ }}
153
+ rows={4}
154
+ cols={50}
155
+ placeholder="Enter text here"
156
+ onChange={onChange}
157
+ value={formData.text || ""}
158
+ ></textarea>
159
+ </div>
160
+
161
+ <div
162
+ style={{
163
+ display: "grid",
164
+ gridTemplateColumns: "1fr 1fr",
165
+ gap: "12px",
166
+ marginBottom: 20,
167
+ }}
168
+ >
169
+ {/* Row 1 */}
170
+ <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
171
+ <label htmlFor="addtext_color">
172
+ <b>Font color</b>
173
+ </label>
174
+ <input
175
+ type="color"
176
+ id="addtext_color"
177
+ style={{ margin: "2px 0" }}
178
+ value={formData.color}
179
+ onChange={onChange}
180
+ />
181
+ </div>
182
+
183
+ <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
184
+ <label htmlFor="addtext_size">
185
+ <b>Font size</b>
186
+ </label>
187
+ <input
188
+ style={{ width: "52px" }}
189
+ type="number"
190
+ id="addtext_size"
191
+ value={formData.size}
192
+ onChange={onChange}
193
+ />
194
+ </div>
195
+
196
+ {/* Row 2 */}
197
+ <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
198
+ <label htmlFor="addtext_bgcolor">
199
+ <b>Background</b>
200
+ </label>
201
+ <input
202
+ type="color"
203
+ id="addtext_bgcolor"
204
+ style={{ margin: "2px 0" }}
205
+ value={formData.bgcolor}
206
+ onChange={onChange}
207
+ />
208
+ </div>
209
+
210
+ <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
211
+ <label htmlFor="addtext_bordercolor">
212
+ <b>Border color</b>
213
+ </label>
214
+ <input
215
+ type="color"
216
+ id="addtext_bordercolor"
217
+ style={{ margin: "2px 0" }}
218
+ value={formData.bordercolor}
219
+ onChange={onChange}
220
+ />
221
+ </div>
222
+
223
+ {/* Row 3 */}
224
+ <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
225
+ <label htmlFor="addtext_arrowcolor">
226
+ <b>Arrow color</b>
227
+ </label>
228
+ <input
229
+ type="color"
230
+ id="addtext_arrowcolor"
231
+ style={{ margin: "2px 0" }}
232
+ value={formData.arrowcolor}
233
+ onChange={onChange}
234
+ />
235
+ </div>
236
+
237
+ <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
238
+ <label htmlFor="addtext_arrowsize">
239
+ <b>Arrow size</b>
240
+ </label>
241
+ <input
242
+ style={{ width: "52px" }}
243
+ type="number"
244
+ id="addtext_arrowsize"
245
+ min="0.1"
246
+ max="5"
247
+ step="0.1"
248
+ value={formData.arrowsize}
249
+ onChange={onChange}
250
+ />
251
+ </div>
252
+
253
+ {/* Row 4 */}
254
+ <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
255
+ <label htmlFor="addtext_arrowwidth">
256
+ <b>Arrow width</b>
257
+ </label>
258
+ <input
259
+ style={{ width: "52px" }}
260
+ type="number"
261
+ id="addtext_arrowwidth"
262
+ min="1"
263
+ max="10"
264
+ value={formData.arrowwidth}
265
+ onChange={onChange}
266
+ />
267
+ </div>
268
+
269
+ <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
270
+ <label htmlFor="addtext_yanchor">
271
+ <b>Position</b>
272
+ </label>
273
+ <select
274
+ id="addtext_yanchor"
275
+ name="yanchor"
276
+ style={{ width: "100px" }}
277
+ value={formData.yanchor}
278
+ onChange={onChange}
279
+ >
280
+ <option value="above">Above</option>
281
+ <option value="below">Below</option>
282
+ </select>
283
+ </div>
284
+ </div>
285
+ </div>
286
+
287
+ <div style={{ float: "right", marginTop: 20 }}>
288
+ <button
289
+ className="_btn-tertiary ph-capture"
290
+ id="title_cancel"
291
+ onClick={onClose}
292
+ >
293
+ Cancel
294
+ </button>
295
+ {editMode && (
296
+ <button
297
+ className="_btn ph-capture"
298
+ id="title_delete"
299
+ onClick={onDelete}
300
+ >
301
+ Delete
302
+ </button>
303
+ )}
304
+ <button
305
+ className="_btn ph-capture"
306
+ id="title_submit"
307
+ onClick={onSubmit}
308
+ >
309
+ Submit
310
+ </button>
311
+ </div>
312
+ </div>
313
+ </CommonDialog>
314
+ );
315
+ }
frontend-components/plotly/src/components/Dialogs/TitleChartDialog.tsx ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import CommonDialog, { styleDialog } from "../Dialogs/CommonDialog";
2
+ import { useState } from "react";
3
+
4
+ export default function TitleChartDialog({
5
+ plotlyData,
6
+ open,
7
+ close,
8
+ defaultTitle,
9
+ updateTitle,
10
+ updateAxesTitles,
11
+ }: {
12
+ plotlyData?: any;
13
+ open: boolean;
14
+ close: () => void;
15
+ defaultTitle: string;
16
+ updateTitle: (title: string) => void;
17
+ updateAxesTitles: (axesTitles: any) => void;
18
+ }) {
19
+ const [title, setTitle] = useState(defaultTitle);
20
+
21
+ const yAxes = Object.keys(plotlyData.layout || {}).filter(
22
+ (k) => k.startsWith("yaxis") && plotlyData.layout[k].range != undefined
23
+ );
24
+ const xAxes = Object.keys(plotlyData.layout || {}).filter(
25
+ (k) =>
26
+ k.startsWith("xaxis") &&
27
+ plotlyData.layout[k].showticklabels != undefined &&
28
+ plotlyData.layout[k]?.anchor
29
+ );
30
+
31
+ const [axesTitles, setAxesTitles] = useState<any>({});
32
+
33
+ return (
34
+ <CommonDialog
35
+ title="Chart Titles"
36
+ description="Change the titles on the chart."
37
+ open={open}
38
+ close={close}
39
+ >
40
+ <div id="popup_title" className="popup_content">
41
+ <div style={{ display: "flex", flexDirection: "column", gap: 0 }}>
42
+ <div>
43
+ <label htmlFor="title_text">
44
+ <b>Title:</b>
45
+ </label>
46
+ <textarea
47
+ id="title_text"
48
+ style={{
49
+ ...styleDialog,
50
+ width: "100%",
51
+ maxWidth: "100%",
52
+ maxHeight: "200px",
53
+ marginTop: "8px",
54
+ marginLeft: "0px",
55
+ }}
56
+ rows={2}
57
+ cols={20}
58
+ value={title}
59
+ onChange={(e) => setTitle(e.target.value)}
60
+ ></textarea>
61
+ </div>
62
+ <div
63
+ id="xaxis_div"
64
+ className="csv_column_container"
65
+ style={{ marginTop: 5, marginBottom: -5 }}
66
+ >
67
+ {xAxes.map((x, i) => (
68
+ <div key={x} style={{ marginTop: 5, marginBottom: 5 }}>
69
+ <label htmlFor={`title_${x}`}>
70
+ {i === 0 ? <b>X axis:</b> : <b>X axis {i + 1}:</b>}
71
+ </label>
72
+ <input
73
+ id={`title_${x}`}
74
+ style={{ marginLeft: "0px", padding: "5px 2px 2px 5px" }}
75
+ type="text"
76
+ defaultValue={plotlyData?.layout[x]?.title?.text || ""}
77
+ onChange={(e) => {
78
+ setAxesTitles({
79
+ ...axesTitles,
80
+ [x]: e.target.value,
81
+ });
82
+ }}
83
+ />
84
+ </div>
85
+ ))}
86
+ </div>
87
+ <div
88
+ id="yaxis_div"
89
+ className="csv_column_container"
90
+ style={{ marginTop: 5, marginBottom: 5 }}
91
+ >
92
+ {yAxes.map((y, i) => (
93
+ <div key={y} style={{ marginTop: 10 }}>
94
+ <label htmlFor={`title_${y}`}>
95
+ {i === 0 ? <b>Y axis:</b> : <b>Y axis {i + 1}:</b>}
96
+ </label>
97
+ <input
98
+ id={`title_${y}`}
99
+ style={{ marginLeft: "0px", padding: "5px 2px 2px 5px" }}
100
+ type="text"
101
+ defaultValue={plotlyData?.layout[y]?.title?.text || ""}
102
+ onChange={(e) => {
103
+ setAxesTitles({
104
+ ...axesTitles,
105
+ [y]: e.target.value,
106
+ });
107
+ }}
108
+ />
109
+ </div>
110
+ ))}
111
+ </div>
112
+ </div>
113
+
114
+ <div style={{ float: "right", marginTop: 20 }}>
115
+ <button
116
+ className="_btn-tertiary ph-capture"
117
+ id="title_cancel"
118
+ onClick={close}
119
+ >
120
+ Cancel
121
+ </button>
122
+ <button
123
+ className="_btn ph-capture"
124
+ id="title_submit"
125
+ onClick={() => {
126
+ // Update parent state - this will trigger the useEffect in Chart.tsx
127
+ updateTitle(title);
128
+ updateAxesTitles(axesTitles);
129
+
130
+ // Force an immediate update to the plotly chart directly
131
+ if (window.Plotly && document.getElementById('plotlyChart')) {
132
+ const chart = document.getElementById('plotlyChart');
133
+
134
+ // Only update axis titles, not the main chart title
135
+ const updateObj: { [key: string]: string } = {};
136
+
137
+ // Add all axis title changes
138
+ Object.entries(axesTitles).forEach(([axis, text]) => {
139
+ updateObj[`${axis}.title.text`] = String(text);
140
+ });
141
+
142
+ if (Object.keys(updateObj).length > 0) {
143
+ console.log("Applying immediate axis title updates:", updateObj);
144
+ window.Plotly.relayout(chart, updateObj);
145
+ }
146
+ }
147
+
148
+ close();
149
+ }}
150
+ >
151
+ Submit
152
+ </button>
153
+ </div>
154
+ </div>
155
+ </CommonDialog>
156
+ );
157
+ }
frontend-components/plotly/src/components/Icons/Close.tsx ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { SVGProps } from "react";
2
+ interface SVGRProps {
3
+ title?: string;
4
+ titleId?: string;
5
+ }
6
+
7
+ const CloseIcon = ({
8
+ title,
9
+ titleId,
10
+ ...props
11
+ }: SVGProps<SVGSVGElement> & SVGRProps) => (
12
+ <svg
13
+ xmlns="http://www.w3.org/2000/svg"
14
+ fill="none"
15
+ viewBox="0 0 24 24"
16
+ stroke="currentColor"
17
+ strokeWidth={1.5}
18
+ {...props}
19
+ >
20
+ <path
21
+ strokeLinecap="round"
22
+ strokeLinejoin="round"
23
+ d="M6 18L18 6M6 6l12 12"
24
+ />
25
+ </svg>
26
+ );
27
+
28
+ export default CloseIcon;