mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-30 05:52:23 +00:00
Risk neutral options valuation (#906)
* Risk neutral hedge * Changeable risk * Added documentation * Improved description * Improved description * Retry Co-authored-by: Colin Delahunty <colindelahunty@Colins-MacBook-Air.local>
This commit is contained in:
@@ -10,14 +10,14 @@ def get_loss_at_strike(strike: float, chain: pd.DataFrame) -> float:
|
||||
|
||||
Parameters
|
||||
----------
|
||||
strike: Union[int,float]
|
||||
strike : Union[int,float]
|
||||
Value to calculate total loss at
|
||||
chain: Dataframe:
|
||||
chain : Dataframe:
|
||||
Dataframe containing at least strike and openInterest
|
||||
|
||||
Returns
|
||||
-------
|
||||
loss: Union[float,int]
|
||||
loss : Union[float,int]
|
||||
Total loss
|
||||
"""
|
||||
|
||||
@@ -38,7 +38,7 @@ def calculate_max_pain(chain: pd.DataFrame) -> int:
|
||||
|
||||
Parameters
|
||||
----------
|
||||
chain: DataFrame
|
||||
chain : DataFrame
|
||||
Dataframe to calculate value from
|
||||
|
||||
Returns
|
||||
@@ -66,7 +66,7 @@ def convert(orig: str, to: str) -> float:
|
||||
"""Convert a string to a specific type of number
|
||||
Parameters
|
||||
----------
|
||||
orig: str
|
||||
orig : str
|
||||
String to convert
|
||||
Returns
|
||||
-------
|
||||
@@ -82,6 +82,35 @@ def convert(orig: str, to: str) -> float:
|
||||
raise ValueError("Invalid to format, please use '%' or ','.")
|
||||
|
||||
|
||||
def rn_payoff(x: str, df: pd.DataFrame, put: bool, delta: int, rf: float) -> float:
|
||||
"""The risk neutral payoff for a stock
|
||||
Parameters
|
||||
----------
|
||||
x : str
|
||||
Strike price
|
||||
df : pd.DataFrame
|
||||
Dataframe of stocks prices and probabilities
|
||||
put : bool
|
||||
Whether the asset is a put or a call
|
||||
delta : int
|
||||
Difference between today's date and expirations date in days
|
||||
rf : float
|
||||
The current risk-free rate
|
||||
|
||||
Returns
|
||||
-------
|
||||
number : float
|
||||
Risk neutral value of option
|
||||
"""
|
||||
if put:
|
||||
df["Gain"] = np.where(x > df["Price"], x - df["Price"], 0)
|
||||
else:
|
||||
df["Gain"] = np.where(x < df["Price"], df["Price"] - x, 0)
|
||||
df["Vals"] = df["Chance"] * df["Gain"]
|
||||
risk_free = (1 + rf) ** (delta / 365)
|
||||
return sum(df["Vals"]) / risk_free
|
||||
|
||||
|
||||
opt_chain_cols = {
|
||||
"lastTradeDate": {"format": "date", "label": "Last Trade Date"},
|
||||
"strike": {"format": "${x:.2f}", "label": "Strike"},
|
||||
|
||||
@@ -10,6 +10,8 @@ from typing import List
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
from colorama import Style
|
||||
import pandas as pd
|
||||
from tabulate import tabulate
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
@@ -75,6 +77,10 @@ class OptionsController:
|
||||
"payoff",
|
||||
"plot",
|
||||
"parity",
|
||||
"add",
|
||||
"rmv",
|
||||
"show",
|
||||
"rnval",
|
||||
]
|
||||
|
||||
CHOICES += CHOICES_COMMANDS
|
||||
@@ -110,6 +116,7 @@ class OptionsController:
|
||||
"cmd",
|
||||
choices=self.CHOICES,
|
||||
)
|
||||
self.prices = pd.DataFrame(columns=["Price", "Chance"])
|
||||
|
||||
def print_help(self):
|
||||
"""Print help."""
|
||||
@@ -153,6 +160,13 @@ Current Expiry: {self.selected_date or None}
|
||||
parity shows whether options are above or below expected price [Yfinance]
|
||||
|
||||
> payoff shows payoff diagram for a selection of options [Yfinance]
|
||||
|
||||
Options Pricing:
|
||||
add add an expected price to the list
|
||||
rmv remove an expected price from the list
|
||||
show show the listed of expected prices
|
||||
rnval risk neutral valuation for an option:
|
||||
|
||||
{Style.RESET_ALL if not colored else ''}"""
|
||||
print(help_text)
|
||||
|
||||
@@ -1178,7 +1192,6 @@ Current Expiry: {self.selected_date or None}
|
||||
prog="parity",
|
||||
description="Shows whether options are over or under valued",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--put",
|
||||
@@ -1227,6 +1240,158 @@ Current Expiry: {self.selected_date or None}
|
||||
)
|
||||
print("")
|
||||
|
||||
@try_except
|
||||
def call_add(self, other_args: List[str]):
|
||||
"""Process add command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="add",
|
||||
description="Adds a price to the list",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--price",
|
||||
type=float,
|
||||
required="-h" not in other_args,
|
||||
dest="price",
|
||||
help="Projected price of the stock at the expiration date",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--chance",
|
||||
type=float,
|
||||
required="-h" not in other_args,
|
||||
dest="chance",
|
||||
help="Chance that the stock is at a given projected price",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser.price in self.prices["Price"].to_list():
|
||||
df = self.prices[(self.prices["Price"] != ns_parser.price)]
|
||||
else:
|
||||
df = self.prices
|
||||
|
||||
new = {"Price": ns_parser.price, "Chance": ns_parser.chance}
|
||||
df = df.append(new, ignore_index=True)
|
||||
self.prices = df.sort_values("Price")
|
||||
|
||||
@try_except
|
||||
def call_rmv(self, other_args: List[str]):
|
||||
"""Process rmv command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="rmv",
|
||||
description="Removes a price from the list",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--price",
|
||||
type=float,
|
||||
required="-h" not in other_args and "-a" not in other_args,
|
||||
dest="price",
|
||||
help="Price you want to remove from the list",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--all",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest="all",
|
||||
help="Remove all prices from the list",
|
||||
)
|
||||
if other_args and "-" not in other_args[0]:
|
||||
other_args.insert(0, "-p")
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser.all:
|
||||
self.prices = pd.DataFrame(columns=["Price", "Chance"])
|
||||
return
|
||||
|
||||
self.prices = self.prices[(self.prices["Price"] != ns_parser.price)]
|
||||
|
||||
@try_except
|
||||
def call_show(self, _):
|
||||
"""Process show command"""
|
||||
print(f"Estimated price(s) of {self.ticker} at {self.selected_date}")
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
self.prices,
|
||||
headers=self.prices.columns,
|
||||
floatfmt=".2f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
)
|
||||
else:
|
||||
print(self.prices.to_string, "\n")
|
||||
print("")
|
||||
|
||||
@try_except
|
||||
def call_rnval(self, other_args: List[str]):
|
||||
"""Process rnval command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="rnval",
|
||||
description="The risk neutral value of the options",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--put",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Show puts instead of calls",
|
||||
)
|
||||
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",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-r",
|
||||
"--risk",
|
||||
type=float,
|
||||
default=None,
|
||||
dest="risk",
|
||||
help="The risk-free rate to use",
|
||||
)
|
||||
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
|
||||
if sum(self.prices["Chance"]) != 1:
|
||||
print("Total chances must equal one\n")
|
||||
return
|
||||
|
||||
yfinance_view.risk_neutral_vals(
|
||||
self.ticker,
|
||||
self.selected_date,
|
||||
ns_parser.put,
|
||||
self.prices,
|
||||
ns_parser.mini,
|
||||
ns_parser.maxi,
|
||||
ns_parser.risk,
|
||||
)
|
||||
|
||||
|
||||
def menu(ticker: str = ""):
|
||||
"""Options Menu."""
|
||||
|
||||
@@ -496,17 +496,17 @@ def show_parity(
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ticker: str
|
||||
ticker : str
|
||||
Ticker to get expirations for
|
||||
exp: str
|
||||
exp : str
|
||||
Expiration to use for options
|
||||
put: bool
|
||||
put : bool
|
||||
Whether to use puts or calls
|
||||
ask: bool
|
||||
ask : bool
|
||||
Whether to use ask or lastPrice
|
||||
mini: float
|
||||
mini : float
|
||||
Minimum strike price to show
|
||||
maxi: float
|
||||
maxi : float
|
||||
Maximum strike price to show
|
||||
|
||||
"""
|
||||
@@ -567,8 +567,8 @@ def show_parity(
|
||||
if maxi is None:
|
||||
maxi = filtered.strike.quantile(0.75)
|
||||
|
||||
filtered = filtered.loc[filtered["strike"] > mini]
|
||||
filtered = filtered.loc[filtered["strike"] < maxi]
|
||||
filtered = filtered.loc[filtered["strike"] >= mini]
|
||||
filtered = filtered.loc[filtered["strike"] <= maxi]
|
||||
|
||||
show = filtered[["strike", diff]].copy()
|
||||
|
||||
@@ -590,3 +590,75 @@ def show_parity(
|
||||
print(show.to_string(index=False))
|
||||
|
||||
print("")
|
||||
|
||||
|
||||
def risk_neutral_vals(
|
||||
ticker: str,
|
||||
exp: str,
|
||||
put: bool,
|
||||
df: pd.DataFrame,
|
||||
mini: float,
|
||||
maxi: float,
|
||||
risk: float,
|
||||
) -> None:
|
||||
"""Prints current options prices and risk neutral values [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
|
||||
df : pd.DataFrame
|
||||
Estimates for stocks prices and probabilities
|
||||
mini : float
|
||||
Minimum strike price to show
|
||||
maxi : float
|
||||
Maximum strike price to show
|
||||
risk : float
|
||||
The risk-free rate for the asset
|
||||
"""
|
||||
if put:
|
||||
chain = get_option_chain(ticker, exp).puts
|
||||
else:
|
||||
chain = get_option_chain(ticker, exp).calls
|
||||
|
||||
r_date = datetime.strptime(exp, "%Y-%m-%d").date()
|
||||
delta = (r_date - date.today()).days
|
||||
vals = []
|
||||
if risk is None:
|
||||
risk = get_rf()
|
||||
for _, row in chain.iterrows():
|
||||
vals.append(
|
||||
[
|
||||
row["strike"],
|
||||
row["lastPrice"],
|
||||
op_helpers.rn_payoff(row["strike"], df, put, delta, risk),
|
||||
]
|
||||
)
|
||||
new_df = pd.DataFrame(vals, columns=["Strike", "Last Price", "Value"], dtype=float)
|
||||
new_df["Difference"] = new_df["Last Price"] - new_df["Value"]
|
||||
|
||||
if mini is None:
|
||||
mini = new_df.Strike.quantile(0.25)
|
||||
if maxi is None:
|
||||
maxi = new_df.Strike.quantile(0.75)
|
||||
|
||||
new_df = new_df[new_df["Strike"] >= mini]
|
||||
new_df = new_df[new_df["Strike"] <= maxi]
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
new_df,
|
||||
headers=[x.title() for x in new_df.columns],
|
||||
tablefmt="fancy_grid",
|
||||
showindex=False,
|
||||
floatfmt=".2f",
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(new_df.to_string(index=False))
|
||||
print("")
|
||||
|
||||
12
gamestonk_terminal/portfolio/portfolios/cash.csv
vendored
Normal file
12
gamestonk_terminal/portfolio/portfolios/cash.csv
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
Name,Type,Quantity,Date,Price,Fees,Premium,Side
|
||||
cash,cash,1.0,2021/1/22,2000.0,,,deposit
|
||||
cash,cash,1.0,2021/2/22,2000.0,,,deposit
|
||||
cash,cash,1.0,2021/3/22,2000.0,,,deposit
|
||||
cash,cash,1.0,2021/4/22,2000.0,,,deposit
|
||||
cash,cash,1.0,2021/5/22,2000.0,,,deposit
|
||||
cash,cash,1.0,2021/6/22,2000.0,,,deposit
|
||||
cash,cash,1.0,2021/7/22,2000.0,,,deposit
|
||||
cash,cash,1.0,2021/8/22,2000.0,,,deposit
|
||||
cash,cash,1.0,2021/9/22,2000.0,,,deposit
|
||||
cash,cash,1.0,2021/10/22,2000.0,,,deposit
|
||||
cash,cash,1.0,2021/11/22,2000.0,,,deposit
|
||||
|
5
gamestonk_terminal/portfolio/portfolios/james.csv
vendored
Normal file
5
gamestonk_terminal/portfolio/portfolios/james.csv
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
Name,Type,Quantity,Date,Price,Fees,Premium,Side
|
||||
cash,cash,1.0,2021/3/24,2000.0,,,deposit
|
||||
aapl,stock,10,2021/3/24,120.7,0,0,Buy
|
||||
msft,stock,3,2021/3/26,232.8,0,0,Buy
|
||||
aapl,stock,5,2021/7/2,139.8,0,0,Sell
|
||||
|
4
gamestonk_terminal/portfolio/portfolios/market.csv
vendored
Normal file
4
gamestonk_terminal/portfolio/portfolios/market.csv
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
Name,Type,Quantity,Date,Price,Fees,Premium,Side
|
||||
spy,stock,1.0,2019/10/21,200.0,,,Buy
|
||||
cash,cash,1.0,2019/10/21,200.0,,,deposit
|
||||
cash,cash,1.0,2021/6/21,200.0,,,withdrawal
|
||||
|
12
website/content/options/add/_index.md
vendored
Normal file
12
website/content/options/add/_index.md
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
```
|
||||
usage: add [-p] [-c] [-h]
|
||||
```
|
||||
|
||||
add a stock price and probability to the list
|
||||
|
||||
```
|
||||
optional arguments:
|
||||
-p PRICE, --PRICE the estimated finishing price of the asset
|
||||
-c CHANCE, --CHANCE the chance of the asset ending at the given price
|
||||
-h, --help show this help message (default: False)
|
||||
```
|
||||
12
website/content/options/rmv/_index.md
vendored
Normal file
12
website/content/options/rmv/_index.md
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
```
|
||||
usage: rmv [-p] [-c] [-h]
|
||||
```
|
||||
|
||||
remove a stock price from the list
|
||||
|
||||
```
|
||||
optional arguments:
|
||||
-p PRICE, --PRICE the estimated finishing price of the asset
|
||||
-a, --all remove all items from the list of tickers
|
||||
-h, --help show this help message (default: False)
|
||||
```
|
||||
28
website/content/options/rnval/_index.md
vendored
Normal file
28
website/content/options/rnval/_index.md
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
```
|
||||
usage: rnval [-p] [-m MIN] [-M MAX] [-r RISK] [-h]
|
||||
```
|
||||
|
||||
calculates the expcected value of a given option by multiplying
|
||||
the payoff at each predicted stock price by the probability of
|
||||
that stock price happening. This expected profit is divided by
|
||||
one plus the risk free rate to determine the value of those
|
||||
future dollars today. The expected payoff for each strike price
|
||||
is then compared with the last traded price to determine if there
|
||||
are differences in the market.
|
||||
|
||||
For example, if you assume the price of AAPL has an equal chance
|
||||
of finishing at $100 or $200 for a given expiration, you could use
|
||||
this to value an option at that expiration. To value a call with a
|
||||
strike of $150, you would calculate the payoff for each stock price,
|
||||
which would be $0 and $50. Then, we caclulate the weighted average
|
||||
payoff of $25. We need to divide this amount by the risk-free rate,
|
||||
assumed to be 0.02. So the value of this option expiring in one year
|
||||
is 25/1.02, or $24.51.
|
||||
```
|
||||
optional arguments:
|
||||
-p, --put flag to calculate put option (default: False)
|
||||
-m MIN, --min MIN min price to look at (default: -1)
|
||||
-M MAX, --max MAX max price to look at (default: -1)
|
||||
-r RISK, --risk RISK use a custom risk-free amount (default: None)
|
||||
-h, --help show this help message (default: False)
|
||||
```
|
||||
10
website/content/options/show/_index.md
vendored
Normal file
10
website/content/options/show/_index.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
```
|
||||
usage: show [-h]
|
||||
```
|
||||
|
||||
show the list of stocks prices and probabilities
|
||||
|
||||
```
|
||||
optional arguments:
|
||||
-h, --help show this help message (default: False)
|
||||
```
|
||||
8
website/data/menu/main.yml
vendored
8
website/data/menu/main.yml
vendored
@@ -551,6 +551,14 @@ main:
|
||||
ref: "/portfolio/portfolio_optimization"
|
||||
- name: parity
|
||||
ref: "/options/parity"
|
||||
- name: add
|
||||
ref: "/options/add"
|
||||
- name: rmv
|
||||
ref: "/options/rmv"
|
||||
- name: show
|
||||
ref: "/options/show"
|
||||
- name: rnval
|
||||
ref: "/options/rnval"
|
||||
- name: payoff
|
||||
ref: "/options/payoff"
|
||||
sub:
|
||||
|
||||
Reference in New Issue
Block a user