* Refactored code, added parity

* Got call price

* Finalized feature

* added documentation

* Added changes except number limit

* Added limit to number of data points

* Updated duplicated variable names

* Added docstrings, updated filtering

Co-authored-by: Colin Delahunty <colindelahunty@Colins-MacBook-Air.local>
Co-authored-by: Colin Delahunty <colindelahunty@Colins-Air.indiana.edu>
This commit is contained in:
Colin Delahunty
2021-10-15 21:50:23 -04:00
committed by GitHub
parent 25ee8c82f1
commit e5ff56c6ae
10 changed files with 190 additions and 31 deletions

View File

@@ -17,6 +17,7 @@ from colorama import Fore, Style
from pandas._config.config import get_option
from pandas.plotting import register_matplotlib_converters
import pandas.io.formats.format
import requests
from screeninfo import get_monitors
from gamestonk_terminal import feature_flags as gtff
from gamestonk_terminal import config_plot as cfgPlot
@@ -711,3 +712,23 @@ def export_data(
print("Wrong export file specified.\n")
print(f"Saved file: {saved_path}\n")
def get_rf() -> float:
"""
Uses the fiscaldata.gov API to get most recent T-Bill rate
Returns
-------
float
The current US T-Bill rate
"""
try:
base = "https://api.fiscaldata.treasury.gov/services/api/fiscal_service"
end = "/v2/accounting/od/avg_interest_rates"
filters = "?filter=security_desc:eq:Treasury Bills&sort=-record_date"
response = requests.get(base + end + filters)
latest = response.json()["data"][0]
return round(float(latest["avg_interest_rate_amt"]) / 100, 8)
except Exception:
return 0.02

View File

@@ -62,6 +62,7 @@ class OptionsController:
"unu",
"stocks",
"payoff",
"parity",
"smile",
]
@@ -138,6 +139,7 @@ Current Expiry: {self.selected_date or None}
hist plot option history [Tradier]
grhist plot option greek history [Syncretism.io]
smile plot the volatility smile for the expiration date [Yfinance]
parity shows whether options are above or below expected price [Yfinance]
> payoff shows payoff diagram for a selection of options [Yfinance]
{Style.RESET_ALL if not colored else ''}"""
print(help_text)
@@ -1076,6 +1078,66 @@ Current Expiry: {self.selected_date or None}
"""Process stocks command"""
return stocks_controller.menu(self.ticker)
def call_parity(self, other_args: List[str]):
"""Process parity command"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="parity",
description="Shows whether options are over or under valued",
)
parser.add_argument(
"-p",
"--put",
action="store_true",
default=False,
dest="put",
help="Shows puts instead of calls",
)
parser.add_argument(
"-a",
"--ask",
action="store_true",
default=False,
dest="ask",
help="Use ask price instead of lastPrice",
)
parser.add_argument(
"-m",
"--min",
type=float,
default=None,
dest="mini",
help="Minimum strike price shown",
)
parser.add_argument(
"-M",
"--max",
type=float,
default=None,
dest="maxi",
help="Maximum strike price shown",
)
try:
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
if not self.ticker and not self.selected_date:
print("Ticker and expiration required. \n")
return
yfinance_view.show_parity(
self.ticker,
self.selected_date,
ns_parser.put,
ns_parser.ask,
ns_parser.mini,
ns_parser.maxi,
)
print("")
except Exception as e:
print(e, "\n")
def menu(ticker: str = ""):
"""Options Menu."""

View File

@@ -4,18 +4,25 @@ __docformat__ = "numpy"
import os
from bisect import bisect_left
from typing import List, Dict, Any
from datetime import datetime, date
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
import yfinance as yf
from tabulate import tabulate
import gamestonk_terminal.config_plot as cfp
import gamestonk_terminal.feature_flags as gtff
from gamestonk_terminal.helper_funcs import export_data, plot_autoscale
from gamestonk_terminal.options import op_helpers, yfinance_model
from gamestonk_terminal.options.yfinance_model import generate_data
from gamestonk_terminal.options.yfinance_model import (
generate_data,
get_option_chain,
get_price,
)
from gamestonk_terminal.helper_funcs import get_rf
def plot_oi(
@@ -448,3 +455,79 @@ def plot_payoff(
plt.legend()
plt.show()
print("")
def show_parity(
ticker: str, exp: str, put: bool, ask: bool, mini: float, maxi: float
) -> None:
"""Prints options and whether they are under or over priced [Source: Yahoo Finance]
Parameters
----------
ticker: str
Ticker to get expirations for
exp: str
Expiration to use for options
put: bool
Whether to use puts or calls
ask: bool
Whether to use ask or lastPrice
mini: float
Minimum strike price to show
maxi: float
Maximum strike price to show
"""
r_date = datetime.strptime(exp, "%Y-%m-%d").date()
delta = (r_date - date.today()).days / 365
rate = ((1 + get_rf()) ** delta) - 1
stock = get_price(ticker)
chain = get_option_chain(ticker, exp)
name = "ask" if ask else "lastPrice"
o_type = "put" if put else "call"
calls = chain.calls[["strike", name]].copy()
calls = calls.rename(columns={name: "callPrice"})
puts = chain.puts[["strike", name]].copy()
puts = puts.rename(columns={name: "putPrice"})
opts = pd.merge(calls, puts, on="strike")
opts = opts.dropna()
opts = opts.loc[opts["callPrice"] * opts["putPrice"] != 0]
opts["callParity"] = opts["putPrice"] + stock - (opts["strike"] / (1 + rate))
opts["putParity"] = (opts["strike"] / (1 + rate)) + opts["callPrice"] - stock
diff = o_type + " Difference"
opts[diff] = opts[o_type + "Price"] - opts[o_type + "Parity"]
opts["distance"] = abs(stock - opts["strike"])
filtered = opts.copy()
if mini is None:
mini = filtered.strike.quantile(0.25)
if maxi is None:
maxi = filtered.strike.quantile(0.75)
filtered = filtered.loc[filtered["strike"] > mini]
filtered = filtered.loc[filtered["strike"] < maxi]
show = filtered[["strike", diff]].copy()
print("Warning: Low volume options may be difficult to trade.\n")
if ask:
print("Warning: Options with no current ask price not shown.\n")
if gtff.USE_TABULATE_DF:
print(
tabulate(
show,
headers=[x.title() for x in show.columns],
tablefmt="fancy_grid",
showindex=False,
floatfmt=".2f",
)
)
else:
print(show.to_string(index=False))
print("")

View File

@@ -2,7 +2,6 @@
__docformat__ = "numpy"
import argparse
import requests
# These are all the possible yfinance properties
l_valid_property_infos = [
@@ -106,16 +105,3 @@ def check_valid_property_type(aproperty: str) -> str:
return aproperty
raise argparse.ArgumentTypeError(f"{aproperty} is not a valid info")
def get_rf() -> float:
"""Uses the fiscaldata.gov API to get most recent T-Bill rate"""
try:
base = "https://api.fiscaldata.treasury.gov/services/api/fiscal_service"
end = "/v2/accounting/od/avg_interest_rates"
filters = "?filter=security_desc:eq:Treasury Bills&sort=-record_date"
response = requests.get(base + end + filters)
latest = response.json()["data"][0]
return round(float(latest["avg_interest_rate_amt"]) / 100, 8)
except Exception:
return 0.02

View File

@@ -16,7 +16,7 @@ from gamestonk_terminal import feature_flags as gtff
from gamestonk_terminal.config_plot import PLOT_DPI
from gamestonk_terminal.helper_funcs import plot_autoscale
from gamestonk_terminal.portfolio.portfolio_optimization import optimizer_model
from gamestonk_terminal.portfolio.portfolio_optimization.optimizer_helper import get_rf
from gamestonk_terminal.helper_funcs import get_rf
d_period = {
"1d": "[1 Day]",

View File

@@ -18,7 +18,7 @@ from gamestonk_terminal.portfolio.portfolio_optimization import (
optimizer_helper,
)
from gamestonk_terminal.portfolio.portfolio_optimization.optimizer_helper import get_rf
from gamestonk_terminal.helper_funcs import get_rf
period_choices = [
"1d",

View File

@@ -11,7 +11,6 @@ from openpyxl.styles import Border, Side, Font, PatternFill, Alignment
from openpyxl import worksheet
import yfinance as yf
import pandas as pd
import requests
opts = Union[int, str, float]
@@ -60,16 +59,6 @@ def set_cell(
ws[cell].number_format = num_form
def get_rf():
"""Uses the fiscaldata.gov API to get most recent T-Bill rate"""
base = "https://api.fiscaldata.treasury.gov/services/api/fiscal_service"
end = "/v2/accounting/od/avg_interest_rates"
filters = "?filter=security_desc:eq:Treasury Bills&sort=-record_date"
response = requests.get(base + end + filters)
latest = response.json()["data"][0]
return latest["avg_interest_rate_amt"]
def get_fama_raw():
"""Gets base Fama French data to calculate risk"""
with urlopen(

View File

@@ -21,6 +21,8 @@ import requests
from gamestonk_terminal.helper_funcs import parse_known_args_and_warn
from gamestonk_terminal.stocks.fundamental_analysis import dcf_model
from gamestonk_terminal.helper_funcs import get_rf
int_or_str = Union[int, str]
@@ -90,7 +92,7 @@ class CreateExcelFA:
self.df_is: pd.DataFrame = self.get_data("IS", self.is_start, True)
self.df_cf: pd.DataFrame = self.get_data("CF", self.cf_start, False)
self.info: pd.DataFrame = yf.Ticker(ticker).info
self.t_bill: float = dcf_model.get_rf()
self.t_bill: float = get_rf()
self.r_ff: int = dcf_model.get_fama_coe(self.ticker)
def create_workbook(self):

View File

@@ -0,0 +1,14 @@
```
usage: parity [-p] [-a] [-m MINIMUM] [-M MAXIMUM] [-h]
```
shows the parity for options. in theory a short plus a stock should equal a call plus a bond.
```
optional arguments:
-a, --ask uses ask price instead of lastPrice (default: False)
-p, --put shows puts instead of calls (default: False)
-m, --min the minimum strike price to be shown (default: None)
-M, --max the maximum strike price to be shown (default: None)
-h, --help show this help message (default: False)
```

View File

@@ -584,8 +584,10 @@ main:
ref: "/options/grhist"
- name: smile
ref: "/options/smile"
- name: portfolio optimization
ref: "/portfolio/portfolio_optimization"
- name: parity
ref: "/options/parity"
- name: payoff
ref: "/options/payoff"
sub:
- name: add
ref: "/options/payoff/add"