Addition of exchange holiday functionality under stocks/th (#3486)

* Addition of exchange holiday calendars using PandasMarketCalendar

* website update for holidays functionality

* Disable pylint too many attributes

* Changes to not show index for dataframe and include metavar

* Setting of default value for holidays

* Merge + black linter

* test fix

Co-authored-by: james <jmaslek11@gmail.com>
Co-authored-by: Jeroen Bouma <jer.bouma@gmail.com>
This commit is contained in:
kulbinderdio
2022-12-13 02:09:09 +00:00
committed by GitHub
parent 5f47ca2734
commit 7e4a657333
7 changed files with 218 additions and 11 deletions

View File

@@ -12,7 +12,8 @@
"open": ["Bursa"],
"closed": ["Bursa"],
"all": ["Bursa"],
"exchange": ["Bursa"]
"exchange": ["Bursa"],
"holidays":["PandasMarketCalendars"]
},
"options": {
"unu": ["FDScanner"],

View File

@@ -164,6 +164,7 @@ en:
stocks/th/closed: show closed markets
stocks/th/all: show all markets
stocks/th/exchange: show one exchange
stocks/th/holidays: show all dates on which there is a market holiday
stocks/disc/pipo: past IPOs dates
stocks/disc/fipo: future IPOs dates
stocks/disc/gainers: show latest top gainers

View File

@@ -0,0 +1,58 @@
import logging
import pandas_market_calendars as mcal
import pandas as pd
from openbb_terminal.decorators import log_start_end
logger = logging.getLogger(__name__)
@log_start_end(log=logger)
def get_all_holiday_exchange_short_names() -> pd.DataFrame:
"""Get all holiday exchanges short names.
Returns
-------
pd.DataFrame
All available exchanges with holiday data (short names)
"""
exchange_short_names = mcal.calendar_registry.get_calendar_names()
df = pd.DataFrame(exchange_short_names, columns=["short_name"])
return df
@log_start_end(log=logger)
def get_exchange_holidays(exchange_symbol: str, year: int) -> pd.DataFrame:
"""Get all short name of each exchange that we hold holiday calendar for.
Parameters
----------
symbol : str
Exchange symbol
year : int
Calendar year
Returns
-------
pd.DataFrame
All available exchanges with holiday data (short names)
"""
cal = mcal.get_calendar(exchange_symbol)
holidays = pd.DataFrame(cal.holidays().holidays)
start_date = str(year) + "-01-01"
end_date = str(year) + "-12-31"
holidays.columns = ["Holiday Date"]
mask = (holidays["Holiday Date"] >= start_date) & (
holidays["Holiday Date"] <= end_date
)
exchange_holidays = holidays.loc[mask].copy()
exchange_holidays["Holiday Date"] = exchange_holidays["Holiday Date"].astype(str)
return exchange_holidays

View File

@@ -0,0 +1,55 @@
import logging
import pandas as pd
from openbb_terminal.decorators import log_start_end
from openbb_terminal.stocks.tradinghours import pandas_market_cal_model
from openbb_terminal.helper_funcs import print_rich_table
from openbb_terminal.rich_config import console
logger = logging.getLogger(__name__)
@log_start_end(log=logger)
def get_all_holiday_exchange_short_names() -> pd.DataFrame:
"""Get all holiday exchanges short names.
Parameters
----------
Returns
-------
pd.DataFrame
All available exchange with holiday data short names
"""
return pandas_market_cal_model.get_all_holiday_exchange_short_names()
@log_start_end(log=logger)
def display_exchange_holidays(exchange_symbol: str, year: int):
"""Display current exchange holiday calendar.
Parameters
----------
symbol : str
Exchange symbol
year : int
Calendar year
"""
exchange = pandas_market_cal_model.get_exchange_holidays(exchange_symbol, year)
if len(exchange) == 0 or exchange.empty:
console.print(
"[red]"
+ "No exchange data loaded.\n"
+ "Make sure you picked a valid exchange symbol and year."
+ "[/red]\n"
)
return
print_rich_table(
exchange,
show_index=False,
title=f"[bold]{exchange_symbol}[/bold]",
)

View File

@@ -6,7 +6,7 @@ import logging
import os
from typing import List
from datetime import date
import pandas as pd
from openbb_terminal.custom_prompt_toolkit import NestedCompleter
@@ -25,16 +25,21 @@ from openbb_terminal.stocks.tradinghours.tradinghours_helper import (
get_exchanges_short_names,
get_fd_equities_list,
)
from openbb_terminal.stocks.tradinghours.pandas_market_cal_view import (
get_all_holiday_exchange_short_names,
display_exchange_holidays,
)
logger = logging.getLogger(__name__)
# pylint: disable=R0902
class TradingHoursController(BaseController):
"""Trading Hours Controller class."""
CHOICES_COMMANDS = ["symbol", "open", "closed", "all", "exchange"]
CHOICES_COMMANDS = ["symbol", "open", "closed", "all", "exchange", "holidays"]
PATH = "/stocks/th/"
FILE_PATH = os.path.join(os.path.dirname(__file__), "README.md")
CHOICES_GENERATION = True
@@ -50,6 +55,9 @@ class TradingHoursController(BaseController):
short_names = short_names_df["short_name"].values
all_short_names = list(short_names) + list(short_names_index)
self.all_exchange_short_names = sorted(list(all_short_names))
self.all_holiday_exchange_short_names = sorted(
list(get_all_holiday_exchange_short_names()["short_name"].values)
)
self.symbol = None
self.symbol_name = None
@@ -75,7 +83,6 @@ class TradingHoursController(BaseController):
if session and obbff.USE_PROMPT_TOOLKIT:
choices: dict = self.choices_default
self.completer = NestedCompleter.from_nested_dict(choices)
def print_help(self):
@@ -92,6 +99,7 @@ class TradingHoursController(BaseController):
mt.add_cmd("closed")
mt.add_cmd("all")
mt.add_cmd("exchange")
mt.add_cmd("holidays")
mt.add_raw("\n")
mt.add_cmd("symbol")
mt.add_raw("\n")
@@ -231,3 +239,56 @@ class TradingHoursController(BaseController):
ns_parser = self.parse_known_args_and_warn(parser, other_args)
if ns_parser:
bursa_view.display_all()
@log_start_end(log=logger)
def call_holidays(self, other_args: List[str]):
"""Process 'holidays' command."""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="exchange",
description="Select the exchange and year you want see holidays for",
)
parser.add_argument(
"-n",
"--name",
help="Exchange short name",
dest="exchange",
required=True,
type=str.upper,
choices=self.all_holiday_exchange_short_names,
metavar="LSE",
)
parser.add_argument(
"-y",
"--year",
help="Year",
nargs="?",
type=int,
const=date.today().year,
dest="year",
metavar="year",
)
if (
other_args
and "-n" not in other_args[0]
and "--name" not in other_args[0]
and "-h" not in other_args
):
other_args.insert(0, "-n")
other_args.insert(2, "-y")
ns_parser = self.parse_known_args_and_warn(parser, other_args)
if ns_parser:
if ns_parser.exchange:
if ns_parser.year:
display_exchange_holidays(ns_parser.exchange, ns_parser.year)
else:
display_exchange_holidays(ns_parser.exchange, date.today().year)
else:
logger.error("Select the exchange and year you want holiday calendar for.")
console.print(
"[red]Select the exchange and year you want holiday calendar for.[/red]\n"
)

View File

@@ -0,0 +1,3 @@
usage: `holidays LSE 2022`
Exchange holiday info

View File

@@ -20,13 +20,14 @@ Choose from: <a href="https://openbb-finance.github.io/OpenBBTerminal/terminal/s
<a href="https://openbb-finance.github.io/OpenBBTerminal/terminal/stocks/tradinghours/closed/" target="_blank" rel="noreferrer noopener">closed</a>,
<a href="https://openbb-finance.github.io/OpenBBTerminal/terminal/stocks/tradinghours/all/" target="_blank" rel="noreferrer noopener">all</a> or
<a href="https://openbb-finance.github.io/OpenBBTerminal/terminal/stocks/tradinghours/exchange/" target="_blank" rel="noreferrer noopener">exchange</a>.
<a href="https://openbb-finance.github.io/OpenBBTerminal/terminal/stocks/tradinghours/holidays/" target="_blank" rel="noreferrer noopener">holidays</a>.
Below, examples are given what these commands display, note that the tables are purposely cut off to keep the
introduction compact.
````
(🦋) /stocks/th/ $ open
Open markets
Open markets
┏━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓
┃ ┃ name ┃ short_name ┃
┡━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩
@@ -49,7 +50,7 @@ introduction compact.
<continues>
(🦋) /stocks/th/ $ closed
Closed markets
Closed markets
┏━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓
┃ ┃ name ┃ short_name ┃
┡━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩
@@ -74,7 +75,7 @@ introduction compact.
<continues>
(🦋) /stocks/th/ $ all
World markets
World markets
┏━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━┓
┃ ┃ name ┃ short_name ┃ open ┃
┡━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━┩
@@ -120,10 +121,10 @@ Market open: False
Selected symbol
Symbol: FB2.L
Name: LEVERAGE SHARES PUBLIC LIMITED
Name: LEVERAGE SHARES PUBLIC LIMITED
Market open: False
(🦋) /stocks/th/ $ exchange BO
(🦋) /stocks/th/ $ exchange BO
┏━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ ┃ BSE ┃
┡━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
@@ -151,8 +152,35 @@ Market open: False
├───────────────────────┼───────────────────────────────────────────────────────────────────────────┤
│ timezone │ Asia/Kolkata │
├───────────────────────┼───────────────────────────────────────────────────────────────────────────┤
│ flag │ 🇮🇳
│ flag │ 🇮🇳
├───────────────────────┼───────────────────────────────────────────────────────────────────────────┤
│ open │ False │
└───────────────────────┴───────────────────────────────────────────────────────────────────────────┘
(🦋) /stocks/th/ $ holidays BATS 2024
BATS
┏━━━━━━┳━━━━━━━━━━━━━━┓
┃ ┃ Holiday Date ┃
┡━━━━━━╇━━━━━━━━━━━━━━┩
│ 1805 │ 2024-01-01 │
├──────┼──────────────┤
│ 1806 │ 2024-01-15 │
├──────┼──────────────┤
│ 1807 │ 2024-02-19 │
├──────┼──────────────┤
│ 1808 │ 2024-03-29 │
├──────┼──────────────┤
│ 1809 │ 2024-05-27 │
├──────┼──────────────┤
│ 1810 │ 2024-07-04 │
├──────┼──────────────┤
│ 1811 │ 2024-09-02 │
├──────┼──────────────┤
│ 1812 │ 2024-11-28 │
├──────┼──────────────┤
│ 1813 │ 2024-12-25 │
└──────┴──────────────┘
````