mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-27 04:31:13 +00:00
Options menu updates (#560)
* Add `calculator` function to show pnl for calls and puts * calculator improvement * breaking up op into different submenus * Yfinance functions/plots added * Refactored Tradier menu into new controller * Added tradier historical options and fix crypto candles * Clean up files and get rid of selenium dependecne * ReadME 1 * calculator plot * Redefine main menu structure to get rid of sub menus. * Merge main + update gst readme * Add coloring to chain command and update readme images * Add historical greeks * added ion * Fix up basic calculator * Clean up error messages and yf defaults when tradier key not present. Extend `exp` to allow date entry * add new line
This commit is contained in:
0
exports/options/.gitkeep
vendored
Normal file
0
exports/options/.gitkeep
vendored
Normal file
17
gamestonk_terminal/README.md
vendored
17
gamestonk_terminal/README.md
vendored
@@ -424,13 +424,18 @@ Command|Description|Source
|
||||
|
||||
Command|Description|Source
|
||||
------ | --------|----
|
||||
`disp` | Display all preset screeners filters
|
||||
`scr` | Output screener options
|
||||
`load` | Load new ticker
|
||||
`info` | Display option information | [Barchart](https://barchart.com/)
|
||||
`calc` | Basic option PnL calculator
|
||||
`exp` | see/set expiry date
|
||||
`volume` | volume + open interest options trading plot |[Yahoo Finance](https://finance.yahoo.com/)
|
||||
`vcalls` | calls volume + open interest plot |[Yahoo Finance](https://finance.yahoo.com/)
|
||||
`vputs` | puts volume + open interest plot |[Yahoo Finance](https://finance.yahoo.com/)
|
||||
`chains` | displays option chains |[Tradier](https://developer.tradier.com/)
|
||||
`info` | display option information | [Barchart](https://barchart.com/)
|
||||
|
||||
`oi` | Plot open interest
|
||||
`vol` | Plot volume
|
||||
`voi` | Plot volume and open interest
|
||||
`hist` | Plot historical options data | [Tradier](https://developer.tradier.com/)
|
||||
`gr_hist` | Plot historical option greek | [Syncretism](ops.syncretism.io)
|
||||
|
||||
|
||||
## Screener [»](screener/README.md)
|
||||
@@ -470,7 +475,7 @@ lis | latest insider sales
|
||||
blis | big latest insider sales ($100k+)
|
||||
blos | big latest officer sales ($100k+)
|
||||
blcs | big latest CEO/CFO sales ($100k+)
|
||||
Top |
|
||||
Top |
|
||||
topt | top officer purchases today
|
||||
toppw | top officer purchases past week
|
||||
toppm | top officer purchases past month
|
||||
|
||||
@@ -62,7 +62,7 @@ def plot_candles(candles_df: pd.DataFrame, title: str):
|
||||
candles_df,
|
||||
type="candle",
|
||||
volume=True,
|
||||
title=f"{title}",
|
||||
title=f"\n{title}",
|
||||
xrotation=20,
|
||||
style="binance",
|
||||
figratio=(10, 7),
|
||||
|
||||
@@ -1319,7 +1319,7 @@ def chart(coin_id: str, other_args: List[str]):
|
||||
]
|
||||
df = df.set_index(pd.to_datetime(df["Time0"])).drop("Time0", axis=1)
|
||||
title = (
|
||||
f"{coin_id}/{ns_parser.vs} from {df.index[0].strftime('%Y/%m/%d')} to {df.index[-1].strftime('%Y/%m/%d')}",
|
||||
f"\n{coin_id}/{ns_parser.vs} from {df.index[0].strftime('%Y/%m/%d')} to {df.index[-1].strftime('%Y/%m/%d')}",
|
||||
)
|
||||
df["Volume"] = df["Volume"] / 1_000_000
|
||||
mpf.plot(
|
||||
|
||||
214
gamestonk_terminal/options/README.md
vendored
214
gamestonk_terminal/options/README.md
vendored
@@ -8,19 +8,27 @@ This menu aims to give insight on options. Options can cause significant share p
|
||||
* Display all preset screeners filters
|
||||
* [scr](#scr)
|
||||
* Output screener options
|
||||
* [exp](#exp)
|
||||
* See/set expiry date [Yahoo Finance]
|
||||
* [voi](#voi)
|
||||
* Volume + open interest options trading plot [Yahoo Finance]
|
||||
* [vcalls](#vcalls)
|
||||
* Calls volume + open interest plot [Yahoo Finance]
|
||||
* [vputs](#vputs)
|
||||
* Puts volume + open interest plot [Yahoo Finance]
|
||||
* [chains](#chains)
|
||||
* Display option chains [Source: Tradier]
|
||||
* [load](#load)
|
||||
* Load new ticker
|
||||
* [info](#info)
|
||||
* _**This requires selenium webdriver installed**_
|
||||
* Display option information [Source: Barchart.com]
|
||||
* [calc](#calc)
|
||||
* basic call/put PnL calculator
|
||||
* [exp](#exp)
|
||||
* See/set expiry date
|
||||
* [chains](#chains)
|
||||
* Display option chains with greeks [Source: Tradier.com]
|
||||
* [oi](#oi)
|
||||
* Plot open interest
|
||||
* [vol](#vol)
|
||||
* Plot volume
|
||||
* [voi](#voi)
|
||||
* Plot volume and open interest
|
||||
* [hist](#hist)
|
||||
* Plot option history [Source: Tradier.com]
|
||||
* [gr_hist](#gr_hist)
|
||||
* Plot historical option greek [Source: ops.syncretism.io]
|
||||
|
||||
|
||||
|
||||
## disp <a name="disp"></a>
|
||||
@@ -35,7 +43,6 @@ View available presets under [presets folder](/gamestonk_terminal/options/preset
|
||||
|
||||
<img width="979" alt="Captura de ecrã 2021-06-27, às 02 32 21" src="https://user-images.githubusercontent.com/25267873/123530365-59fac080-d6f1-11eb-85e1-536f7e927308.png">
|
||||
|
||||
|
||||
## scr <a name="scr"></a>
|
||||
|
||||
```text
|
||||
@@ -50,67 +57,64 @@ Day Low; SMDH: Regular Market Day High; LU: Last Trade Date; LC: Last Crawl; ITM
|
||||
|
||||
<img width="1220" alt="Captura de ecrã 2021-06-27, às 02 31 28" src="https://user-images.githubusercontent.com/25267873/123530368-5e26de00-d6f1-11eb-9a23-0b481b5efae1.png">
|
||||
|
||||
## load <a name="load"></a>
|
||||
Load new ticker
|
||||
```python
|
||||
usage: load [-t Ticker]
|
||||
```
|
||||
* -t/--ticker: Ticker to load. Flag is optional, as `load aapl` will also work
|
||||
|
||||
## info <a name="info"></a>
|
||||
This scrapes the options information from barchart.com/stocks overview. This includes information such as Historical Volatility and IV Rank.
|
||||
|
||||
````
|
||||
usage: info
|
||||
````
|
||||
|
||||
<img width="989" alt="opinfo" src="https://user-images.githubusercontent.com/25267873/115787028-80cb0c80-a3b9-11eb-97a4-ca208aed3be8.png">
|
||||
|
||||
## calc <a name="calc"></a>
|
||||
Basic profit calculator for options
|
||||
```python
|
||||
usage: calc [-s STRIKE] [-p PREMIUM] [--put] [--sell] [-h]
|
||||
```
|
||||
* -s/--strike : Strike price to consider
|
||||
* -p/--premium: Premium being paid
|
||||
* --put: Flag to calculate for put option
|
||||
* --sell: Flag to calculate for selling option
|
||||
|
||||

|
||||
|
||||
## exp <a name="exp"></a>
|
||||
|
||||
See/set expiry dates.
|
||||
```text
|
||||
usage: exp [-d {0,1,2,3,4,5,6,7,8,9,10,11}]
|
||||
usage: exp [-d] [-D
|
||||
```
|
||||
|
||||
See/set expiry dates. [Source: Yahoo Finance]
|
||||
|
||||
* -d : Expiry date index to set.
|
||||
* -D : Date to set
|
||||
|
||||
<img width="955" alt="exp" src="https://user-images.githubusercontent.com/25267873/115161875-f6706900-a097-11eb-8566-bb7b408856a5.png">
|
||||
Running `exp` will show available option expirations
|
||||
```python
|
||||
(✨) (op)> exp
|
||||
|
||||
|
||||
## voi <a name="voi"></a>
|
||||
|
||||
```text
|
||||
usage: voi [-v MIN_VOL] [-m MIN_SP] [-M MAX_SP]
|
||||
Available expiry dates:
|
||||
0. 2021-07-30
|
||||
1. 2021-08-06
|
||||
2. 2021-08-13
|
||||
3. 2021-08-20
|
||||
4. 2021-08-27
|
||||
5. 2021-09-03
|
||||
```
|
||||
|
||||
Plots Volume + Open Interest of calls vs puts. [Source: Yahoo Finance]
|
||||
|
||||
* -v : Minimum volume (considering open interest) threshold of the plot.
|
||||
* -m : Minimum strike price to consider in the plot.
|
||||
* -M : Maximum strike price to consider in the plot.
|
||||
|
||||

|
||||
|
||||
|
||||
## vcalls <a name="vcalls"></a>
|
||||
|
||||
```text
|
||||
usage: vcalls [-m MIN_SP] [-M MAX_SP]
|
||||
To set 2021-08-13, the following two options are equivalent
|
||||
```python
|
||||
exp 2
|
||||
exp -D 2021-08-13
|
||||
```
|
||||
|
||||
Plots Calls Volume + Open Interest. [Source: Yahoo Finance]
|
||||
|
||||
* -m : Minimum strike price to consider in the plot.
|
||||
* -M : Maximum strike price to consider in the plot.
|
||||
|
||||

|
||||
|
||||
|
||||
## vputs <a name="vputs"></a>
|
||||
|
||||
```text
|
||||
usage: vputs [-m MIN_SP] [-M MAX_SP]
|
||||
```
|
||||
|
||||
Plots Puts Volume + Open Interest. [Source: Yahoo Finance]
|
||||
|
||||
* -m : Minimum strike price to consider in the plot.
|
||||
* -M : Maximum strike price to consider in the plot.
|
||||
|
||||

|
||||
|
||||
|
||||
## chains <a name="chains"></a>
|
||||
|
||||
````
|
||||
usage: chains [--calls] [--puts] [-m MIN_SP] [-M MAX_SP]
|
||||
usage: chains [--calls] [--puts] [-m MIN_SP] [-M MAX_SP] [-d] [--export]
|
||||
````
|
||||
|
||||
Display options chains. [Source: Tradier]
|
||||
@@ -119,23 +123,81 @@ Display options chains. [Source: Tradier]
|
||||
* --puts : Flag to show puts only
|
||||
* -m : Minimum strike price to consider.
|
||||
* -M : Maximum strike price to consider.
|
||||
* -d : Columns to display. Should be comma separated. Must be in * -d : Columns to display from the following selection: bid, ask, strike, bidsize, asksize, volume, open_interest, delta, gamma, theta, vega, ask_iv, bid_iv, mid_iv. Default: mid_iv,vega_theta_gamma,delta,volume,open_interest,bid,ask. Note that they are separated by comma.
|
||||
* -d : Columns to display. Should be comma separated. Must be in * -d : Columns to display from the following selection: symbol, bid, ask, strike, bidsize, asksize, volume, open_interest, delta, gamma, theta, vega, iv. Note that they are separated by comma.
|
||||
* --export: Flag to export. Options are {csv,json,xlsx}.
|
||||
|
||||

|
||||
|
||||
## oi <a name="oi"></a>
|
||||
Plot open interest.
|
||||
```python
|
||||
usage: oi [-m MIN] [-M MAX] [--calls] [--puts] [--source {tr,yf}] [-h]
|
||||
```
|
||||
* -m/--min: Min strike to plot. Defaults to .75 * current price
|
||||
* -M/--max: Max strike to plot. Defaults to 1.25 * current price
|
||||
* --calls: Flag to plot calls only
|
||||
* --puts: Flag to plot puts only
|
||||
* --source: Data source. Can be ['yf','tr']. Defaults to 'tr' for Tradier.
|
||||
|
||||

|
||||
|
||||
## vol <a name="vol"></a>
|
||||
Plot volume.
|
||||
```python
|
||||
usage: vol [-m MIN] [-M MAX] [--calls] [--puts] [--source {tr,yf}] [-h]
|
||||
```
|
||||
* -m/--min: Min strike to plot. Defaults to .75 * current price
|
||||
* -M/--max: Max strike to plot. Defaults to 1.25 * current price
|
||||
* --calls: Flag to plot calls only
|
||||
* --puts: Flag to plot puts only
|
||||
* --source: Data source. Can be ['yf','tr']. Defaults to 'tr' for Tradier.
|
||||

|
||||
|
||||
## voi <a name="voi"></a>
|
||||
|
||||
```text
|
||||
usage: voi [-v MIN_VOL] [-m MIN_SP] [-M MAX_SP] [--source]
|
||||
```
|
||||
|
||||
Plots Volume + Open Interest of calls vs puts.
|
||||
|
||||
* -v : Minimum volume (considering open interest) threshold of the plot.
|
||||
* -m : Minimum strike price to consider in the plot.
|
||||
* -M : Maximum strike price to consider in the plot.
|
||||
* --source: Data source. Can be ['yf','tr']. Defaults to 'tr' for Tradier.
|
||||
|
||||

|
||||
|
||||
## hist <a name="hist"></a>
|
||||
Shows historical option chain
|
||||
```python
|
||||
usage: hist [-s STRIKE] [--put] [--chain CHAIN_ID] [--raw] [--export {csv,json,xlsx}] [-h]
|
||||
```
|
||||
* -s/--strike: Strike to show history for
|
||||
* --put: Flag to indicate the option is a put
|
||||
* --chain: OCC Chain ID. Example: AAPL210730C00144000. This flag overwrites the strike and put option
|
||||
* --raw: Show raw output
|
||||
* --export: Export file. Can be {csv, json, xlsx}.
|
||||
|
||||
Note that the chain ID can be obtained by using `chains -d symbol`
|
||||
|
||||

|
||||
|
||||
|
||||
<img width="948" alt="chains" src="https://user-images.githubusercontent.com/25267873/115161876-f708ff80-a097-11eb-8073-195979862a45.png">
|
||||
## gr_hist <a name="gr_hist"></a>
|
||||
Plot historical option greeks. Data from ops.syncretism.io
|
||||
|
||||
```python
|
||||
usage: gr_hist [-s STRIKE] [--put] [-g {iv,gamma,theta,vega,delta,rho,premium}] [--chain CHAIN_ID] [--raw] [--export {csv,json,xlsx}] [-h]
|
||||
```
|
||||
* -s/--strike: Strike to show history for
|
||||
* -g/--greek: Greek to get data for. Can be {iv,gamma,theta,vega,delta,rho}. Also can be premium
|
||||
* --put: Flag to indicate the option is a put
|
||||
* --chain: OCC Chain ID. Example: AAPL210730C00144000. This flag overwrites the strike and put option
|
||||
* --raw: Show raw output (last 20 entries)
|
||||
* --export: Export file. Can be {csv, json, xlsx}.
|
||||
|
||||
## info <a name="info"></a>
|
||||
This scrapes the options information from barchart.com/stocks overview. This includes information such as Historical Volatility and IV Rank.
|
||||
|
||||
In order to run, the selenium webdriver must be installed.
|
||||
Currently, this runs on either Chrome or Firefox. The path to the driver should be defined in [config_terminal.py](#config_terminal.py).
|
||||
|
||||
Note this may take more time than other commands to process.
|
||||
|
||||
````
|
||||
usage: info [-d DRIVER]
|
||||
````
|
||||
* -d/--driver : One of {chrome, firefox}. This indicates which driver you have installed.
|
||||
|
||||
<img width="989" alt="opinfo" src="https://user-images.githubusercontent.com/25267873/115787028-80cb0c80-a3b9-11eb-97a4-ca208aed3be8.png">
|
||||
```python
|
||||
(✨) (op)> gr_hist -s 145 --put -g delta
|
||||
```
|
||||

|
||||
|
||||
0
gamestonk_terminal/options/__init__.py
Normal file
0
gamestonk_terminal/options/__init__.py
Normal file
@@ -3,20 +3,13 @@ __docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
from typing import List
|
||||
from selenium import webdriver
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
# from selenium.webdriver.chrome.options import Options as cOpts
|
||||
# from selenium.webdriver.firefox.options import Options as fOpts
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
import pandas as pd
|
||||
from tabulate import tabulate
|
||||
from gamestonk_terminal.config_terminal import (
|
||||
PATH_TO_SELENIUM_DRIVER as path_to_driver,
|
||||
WEBDRIVER_TO_USE as web,
|
||||
)
|
||||
from gamestonk_terminal.helper_funcs import parse_known_args_and_warn
|
||||
|
||||
browsers = ["chrome", "firefox"]
|
||||
from gamestonk_terminal.helper_funcs import parse_known_args_and_warn
|
||||
|
||||
|
||||
def print_options_data(stock: str, other_args: List[str]):
|
||||
@@ -29,9 +22,6 @@ def print_options_data(stock: str, other_args: List[str]):
|
||||
other_args: List[str]
|
||||
Other arguments. Currently just accepts a browser flag for selenium
|
||||
"""
|
||||
if path_to_driver is None:
|
||||
print("Please specify your selenium driver path in config_terminal.py", "\n")
|
||||
return
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
@@ -39,38 +29,16 @@ def print_options_data(stock: str, other_args: List[str]):
|
||||
prog="info",
|
||||
description="Display option data [Source: Barchart.com]",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-b",
|
||||
"--browser",
|
||||
dest="browser",
|
||||
help="selenium webdriver to use",
|
||||
choices=browsers,
|
||||
default=web,
|
||||
)
|
||||
|
||||
try:
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if ns_parser.browser:
|
||||
browser = ns_parser.browser
|
||||
|
||||
if browser == "chrome":
|
||||
# commenting this because it breaks when in usage
|
||||
# the downside is that the browser will pop up to get the data
|
||||
# options = cOpts()
|
||||
# options.headless = True
|
||||
driver = webdriver.Chrome(executable_path=path_to_driver)
|
||||
|
||||
elif browser == "firefox":
|
||||
# commenting this because it breaks when in usage
|
||||
# the downside is that the browser will pop up to get the data
|
||||
# options = fOpts()
|
||||
# options.headless = True
|
||||
driver = webdriver.Firefox(executable_path=path_to_driver)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
page = f"https://www.barchart.com/stocks/quotes/{stock}/overview"
|
||||
driver.get(page)
|
||||
|
||||
soup = BeautifulSoup(driver.page_source, "html.parser")
|
||||
r = requests.get(page, headers={"User-Agent": "Mozilla/5.0"})
|
||||
soup = BeautifulSoup(r.text, "html.parser")
|
||||
tags = soup.find(
|
||||
"div",
|
||||
attrs={
|
||||
@@ -80,7 +48,6 @@ def print_options_data(stock: str, other_args: List[str]):
|
||||
data = tags.find_all("li")
|
||||
labels = []
|
||||
values = []
|
||||
print("")
|
||||
for row in data:
|
||||
labels.append(row.find_all("span")[0].getText())
|
||||
values.append(row.find_all("span")[1].getText())
|
||||
|
||||
143
gamestonk_terminal/options/calculator_model.py
Normal file
143
gamestonk_terminal/options/calculator_model.py
Normal file
@@ -0,0 +1,143 @@
|
||||
"""Calculator Model"""
|
||||
___docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
from typing import List
|
||||
|
||||
import numpy as np
|
||||
from matplotlib import pyplot as plt
|
||||
|
||||
from gamestonk_terminal.helper_funcs import parse_known_args_and_warn, plot_autoscale
|
||||
from gamestonk_terminal import config_plot as cfp
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
|
||||
|
||||
def pnl_calculator(other_args: List[str]):
|
||||
"""Plot profit/loss for different option variables
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other_args: List[str]
|
||||
Argparse arguments
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="calc",
|
||||
description="Calculate profit or loss for given option settings.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--put",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest="put",
|
||||
help="Flag to calculate put option",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--sell",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest="sell",
|
||||
help="Flag to get profit chart of selling contract",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--strike",
|
||||
type=float,
|
||||
dest="strike",
|
||||
help="Option strike price",
|
||||
default=10,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-p", "--premium", type=float, dest="premium", help="Premium price", default=1
|
||||
)
|
||||
|
||||
try:
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
strike = ns_parser.strike
|
||||
premium = ns_parser.premium
|
||||
|
||||
price_at_expiry = np.linspace(ns_parser.strike / 2, 1.5 * ns_parser.strike, 301)
|
||||
|
||||
sell = [1, -1][ns_parser.sell]
|
||||
if ns_parser.put:
|
||||
break_even = strike - sell * premium
|
||||
pnl = strike - premium - price_at_expiry
|
||||
pnl = sell * 100 * np.where(price_at_expiry < strike, pnl, -premium)
|
||||
|
||||
else:
|
||||
break_even = strike + sell * premium
|
||||
pnl = price_at_expiry - strike - premium
|
||||
pnl = sell * 100 * np.where(price_at_expiry > strike, pnl, -premium)
|
||||
|
||||
fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfp.PLOT_DPI)
|
||||
ax.plot(price_at_expiry, pnl, alpha=0.1, c="k")
|
||||
ax.fill_between(
|
||||
price_at_expiry, 0, pnl, where=(pnl > 0), facecolor="green", alpha=0.5
|
||||
)
|
||||
ax.fill_between(
|
||||
price_at_expiry, 0, pnl, where=(pnl < 0), facecolor="red", alpha=0.5
|
||||
)
|
||||
ax.axvline(
|
||||
x=break_even, c="black", lw=3, alpha=0.6, label=f"Breakeven: ${break_even}"
|
||||
)
|
||||
ax.axvline(
|
||||
x=strike, c="dodgerblue", lw=3, alpha=0.6, label=f"Strike: ${strike}"
|
||||
)
|
||||
if ns_parser.sell:
|
||||
ax.axhline(
|
||||
y=100 * premium,
|
||||
c="seagreen",
|
||||
lw=3,
|
||||
alpha=0.6,
|
||||
label=f"Max Profit: ${100*premium}",
|
||||
)
|
||||
else:
|
||||
ax.axhline(
|
||||
y=-100 * premium,
|
||||
c="firebrick",
|
||||
lw=3,
|
||||
alpha=0.6,
|
||||
label=f"Max Loss: ${-100 * premium}",
|
||||
)
|
||||
|
||||
ax.set_xlabel("Price at Expiry")
|
||||
|
||||
ax.set_ylabel("Profit")
|
||||
ax.set_title(
|
||||
f"Profit for {['Buying','Selling'][ns_parser.sell]} {['Call', 'Put'][ns_parser.put]} option"
|
||||
)
|
||||
ax.grid(True)
|
||||
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
|
||||
plt.legend(loc=0)
|
||||
fig.tight_layout(pad=1)
|
||||
plt.show()
|
||||
print(f"Strike: ${strike}")
|
||||
print(f"Premium: ${premium}")
|
||||
print(f"Breakeven price: ${break_even}")
|
||||
if ns_parser.sell:
|
||||
print(f"Max profit: ${100 * premium}")
|
||||
print("Max loss: Unlimited")
|
||||
else:
|
||||
print("Max profit: Unlimited")
|
||||
print(f"Max loss: ${-100*premium}")
|
||||
print("")
|
||||
|
||||
except Exception as e:
|
||||
print(e, "\n")
|
||||
return
|
||||
|
||||
except SystemExit:
|
||||
print("")
|
||||
return
|
||||
@@ -5,18 +5,22 @@ import argparse
|
||||
import os
|
||||
from typing import List
|
||||
import matplotlib.pyplot as plt
|
||||
import pandas as pd
|
||||
|
||||
import yfinance as yf
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
from gamestonk_terminal.helper_funcs import get_flair, parse_known_args_and_warn
|
||||
from gamestonk_terminal.helper_funcs import get_flair
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
from gamestonk_terminal.options import (
|
||||
yahoo_view,
|
||||
tradier_view,
|
||||
barchart_view,
|
||||
syncretism_view,
|
||||
calculator_model,
|
||||
op_helpers,
|
||||
yfinance_view,
|
||||
yfinance_model,
|
||||
tradier_view,
|
||||
tradier_model,
|
||||
)
|
||||
|
||||
from gamestonk_terminal.config_terminal import TRADIER_TOKEN
|
||||
from gamestonk_terminal.menu import session
|
||||
|
||||
|
||||
@@ -32,86 +36,39 @@ class OptionsController:
|
||||
"quit",
|
||||
"disp",
|
||||
"scr",
|
||||
]
|
||||
|
||||
CHOICES_TICKER_DEPENDENT = [
|
||||
"exp",
|
||||
"voi",
|
||||
"vcalls",
|
||||
"vputs",
|
||||
"chains",
|
||||
"calc",
|
||||
"yf",
|
||||
"tr",
|
||||
"info",
|
||||
"load",
|
||||
"exp",
|
||||
"vol",
|
||||
"voi",
|
||||
"oi",
|
||||
"hist",
|
||||
"chains",
|
||||
"gr_hist",
|
||||
]
|
||||
|
||||
def __init__(self, ticker: str, stock: pd.DataFrame):
|
||||
def __init__(self, ticker: str):
|
||||
"""Construct data."""
|
||||
if ticker:
|
||||
self.ticker = ticker
|
||||
self.yf_ticker_data = yf.Ticker(self.ticker)
|
||||
self.expiry_date = self.yf_ticker_data.options[0]
|
||||
self.options = self.yf_ticker_data.option_chain(self.expiry_date)
|
||||
self.last_adj_close_price = stock["Adj Close"].values[-1]
|
||||
|
||||
self.op_parser = argparse.ArgumentParser(add_help=False, prog="op")
|
||||
self.op_parser.add_argument(
|
||||
"cmd",
|
||||
choices=self.CHOICES + self.CHOICES_TICKER_DEPENDENT,
|
||||
)
|
||||
else:
|
||||
self.expiry_date = ""
|
||||
self.op_parser = argparse.ArgumentParser(add_help=False, prog="op")
|
||||
self.op_parser.add_argument(
|
||||
"cmd",
|
||||
choices=self.CHOICES,
|
||||
)
|
||||
|
||||
def expiry_dates(self, other_args: List[str]):
|
||||
"""Print all available expiry dates."""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="exp",
|
||||
description="""See/set expiry dates. [Source: Yahoo Finance]""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--date",
|
||||
dest="n_date",
|
||||
action="store",
|
||||
type=int,
|
||||
default=-1,
|
||||
choices=range(len(self.yf_ticker_data.options)),
|
||||
help=f"Expiry date index for {self.ticker}.",
|
||||
)
|
||||
|
||||
try:
|
||||
if other_args:
|
||||
if "-" not in other_args[0]:
|
||||
other_args.insert(0, "-d")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
# Print possible expiry dates
|
||||
if ns_parser.n_date == -1:
|
||||
print("\nAvailable expiry dates:")
|
||||
for i, d in enumerate(self.yf_ticker_data.options):
|
||||
print(f" {(2-len(str(i)))*' '}{i}. {d}")
|
||||
|
||||
# It means an expiry date was correctly selected
|
||||
self.ticker = ticker
|
||||
if self.ticker:
|
||||
if TRADIER_TOKEN == "REPLACE_ME":
|
||||
self.expiry_dates = yfinance_model.option_expirations(self.ticker)
|
||||
else:
|
||||
self.expiry_date = self.yf_ticker_data.options[ns_parser.n_date]
|
||||
self.options = self.yf_ticker_data.option_chain(self.expiry_date)
|
||||
print(f"\nSelected expiry date : {self.expiry_date}")
|
||||
self.expiry_dates = tradier_model.option_expirations(self.ticker)
|
||||
else:
|
||||
self.expiry_dates = []
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
self.selected_date = ""
|
||||
self.op_parser = argparse.ArgumentParser(add_help=False, prog="op")
|
||||
self.op_parser.add_argument(
|
||||
"cmd",
|
||||
choices=self.CHOICES,
|
||||
)
|
||||
|
||||
print("")
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def print_help(expiry_date):
|
||||
def print_help(self):
|
||||
"""Print help."""
|
||||
print(
|
||||
"https://github.com/GamestonkTerminal/GamestonkTerminal/tree/main/gamestonk_terminal/options"
|
||||
@@ -125,18 +82,22 @@ class OptionsController:
|
||||
print(" disp display all preset screeners filters")
|
||||
print(" scr output screener options")
|
||||
print("")
|
||||
if expiry_date:
|
||||
print(f"Selected expiry date: {expiry_date}")
|
||||
print("")
|
||||
print(" exp see/set expiry date")
|
||||
print(" voi volume + open interest options trading plot")
|
||||
print(" vcalls calls volume + open interest plot")
|
||||
print(" vputs puts volume + open interest plot")
|
||||
print("")
|
||||
print(" chains display option chains")
|
||||
print(
|
||||
" info display option information (volatility, IV rank etc)"
|
||||
)
|
||||
print(f"Current Ticker: {self.ticker or None}")
|
||||
print(" load load new ticker")
|
||||
print(" info display option information (volatility, IV rank etc)")
|
||||
print("")
|
||||
print(" calc basic call/put PnL calculator")
|
||||
print("")
|
||||
print(f"Current Expiration: {self.selected_date or None}")
|
||||
print(" exp see and set expiration dates")
|
||||
print("")
|
||||
if self.selected_date and self.ticker:
|
||||
print(" chains display option chains with greeks [Tradier]]")
|
||||
print(" oi plot open interest [Tradier/YF]")
|
||||
print(" vol plot volume [Tradier/YF]")
|
||||
print(" voi plot volume and open interest [Tradier/YF]")
|
||||
print(" hist plot option history [Tradier")
|
||||
print(" gr_hist plot option greek history [Syncretism]")
|
||||
print("")
|
||||
|
||||
def switch(self, an_input: str):
|
||||
@@ -159,7 +120,7 @@ class OptionsController:
|
||||
|
||||
# Help menu again
|
||||
if known_args.cmd == "?":
|
||||
self.print_help(self.expiry_date)
|
||||
self.print_help()
|
||||
return None
|
||||
|
||||
# Clear screen
|
||||
@@ -173,7 +134,7 @@ class OptionsController:
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process Help command."""
|
||||
self.print_help(self.expiry_date)
|
||||
self.print_help()
|
||||
|
||||
def call_q(self, _):
|
||||
"""Process Q command - quit the menu."""
|
||||
@@ -183,59 +144,114 @@ class OptionsController:
|
||||
"""Process Quit command - quit the program."""
|
||||
return True
|
||||
|
||||
def call_exp(self, other_args: List[str]):
|
||||
"""Process exp command."""
|
||||
self.expiry_dates(other_args)
|
||||
def call_calc(self, other_args: List[str]):
|
||||
"""Process calc command"""
|
||||
calculator_model.pnl_calculator(other_args)
|
||||
|
||||
def call_voi(self, other_args: List[str]):
|
||||
"""Process voi command."""
|
||||
yahoo_view.plot_volume_open_interest(
|
||||
other_args,
|
||||
self.ticker,
|
||||
self.expiry_date,
|
||||
self.last_adj_close_price,
|
||||
self.options.calls,
|
||||
self.options.puts,
|
||||
)
|
||||
|
||||
def call_vcalls(self, other_args: List[str]):
|
||||
"""Process vcalls command."""
|
||||
yahoo_view.plot_calls_volume_open_interest(
|
||||
other_args,
|
||||
self.ticker,
|
||||
self.expiry_date,
|
||||
self.last_adj_close_price,
|
||||
self.options.calls,
|
||||
)
|
||||
|
||||
def call_vputs(self, other_args: List[str]):
|
||||
"""Process vcalls command."""
|
||||
yahoo_view.plot_puts_volume_open_interest(
|
||||
other_args,
|
||||
self.ticker,
|
||||
self.expiry_date,
|
||||
self.last_adj_close_price,
|
||||
self.options.puts,
|
||||
)
|
||||
|
||||
def call_chains(self, other_args):
|
||||
tradier_view.display_chains(self.ticker, self.expiry_date, other_args)
|
||||
|
||||
def call_info(self, other_args):
|
||||
def call_info(self, other_args: List[str]):
|
||||
"""Process info command"""
|
||||
barchart_view.print_options_data(self.ticker, other_args)
|
||||
|
||||
def call_disp(self, other_args):
|
||||
def call_disp(self, other_args: List[str]):
|
||||
"""Process disp command"""
|
||||
syncretism_view.view_available_presets(other_args)
|
||||
|
||||
def call_scr(self, other_args):
|
||||
def call_scr(self, other_args: List[str]):
|
||||
"""Process scr command"""
|
||||
syncretism_view.screener_output(other_args)
|
||||
|
||||
def call_load(self, other_args: List[str]):
|
||||
"""Process load command"""
|
||||
self.ticker = op_helpers.load(other_args)
|
||||
if TRADIER_TOKEN == "REPLACE_ME":
|
||||
self.expiry_dates = yfinance_model.option_expirations(self.ticker)
|
||||
else:
|
||||
self.expiry_dates = tradier_model.option_expirations(self.ticker)
|
||||
|
||||
def menu(ticker: str, stock: pd.DataFrame):
|
||||
def call_exp(self, other_args: List[str]):
|
||||
"""Process exp command"""
|
||||
if self.ticker:
|
||||
self.selected_date = op_helpers.select_option_date(
|
||||
self.expiry_dates, other_args
|
||||
)
|
||||
else:
|
||||
print("Please select a ticker using load {ticker}", "\n")
|
||||
|
||||
def call_hist(self, other_args: List[str]):
|
||||
if TRADIER_TOKEN != "REPLACE_ME":
|
||||
tradier_view.display_historical(self.ticker, self.selected_date, other_args)
|
||||
else:
|
||||
print("TRADIER TOKEN not supplied. \n")
|
||||
|
||||
def call_chains(self, other_args: List[str]):
|
||||
"""Process chains command"""
|
||||
if TRADIER_TOKEN != "REPLACE_ME":
|
||||
tradier_view.display_chains(self.ticker, self.selected_date, other_args)
|
||||
else:
|
||||
print("TRADIER TOKEN not supplied. \n")
|
||||
|
||||
def call_vol(self, other_args: List[str]):
|
||||
"""Process vol command"""
|
||||
if not self.ticker and not self.selected_date:
|
||||
print("Ticker and expiration required.\n")
|
||||
return
|
||||
parsed = op_helpers.vol(other_args)
|
||||
if not parsed:
|
||||
return
|
||||
if parsed.source == "tr" and TRADIER_TOKEN != "REPLACE_ME":
|
||||
options = tradier_model.get_option_chains(self.ticker, self.selected_date)
|
||||
tradier_view.plot_vol(options, self.ticker, self.selected_date, parsed)
|
||||
else:
|
||||
options = yfinance_model.get_option_chain(self.ticker, self.selected_date)
|
||||
yfinance_view.plot_vol(
|
||||
options.calls, options.puts, self.ticker, self.selected_date, parsed
|
||||
)
|
||||
|
||||
def call_voi(self, other_args: List[str]):
|
||||
"""Process voi command"""
|
||||
if not self.ticker and not self.selected_date:
|
||||
print("Ticker and expiration required.")
|
||||
return
|
||||
parsed = op_helpers.voi(other_args)
|
||||
if not parsed:
|
||||
return
|
||||
if parsed.source == "tr" and TRADIER_TOKEN != "REPLACE_ME":
|
||||
options = tradier_model.get_option_chains(self.ticker, self.selected_date)
|
||||
tradier_view.plot_volume_open_interest(
|
||||
self.ticker, self.selected_date, options, parsed
|
||||
)
|
||||
else:
|
||||
options = yfinance_model.get_option_chain(self.ticker, self.selected_date)
|
||||
yfinance_view.plot_volume_open_interest(
|
||||
self.ticker, self.selected_date, options.calls, options.puts, parsed
|
||||
)
|
||||
|
||||
def call_oi(self, other_args: List[str]):
|
||||
"""Process oi command"""
|
||||
if not self.ticker and not self.selected_date:
|
||||
print("Ticker and expiration required.")
|
||||
return
|
||||
parsed = op_helpers.oi(other_args)
|
||||
if not parsed:
|
||||
return
|
||||
if parsed.source == "tr" and TRADIER_TOKEN != "REPLACE_ME":
|
||||
options = tradier_model.get_option_chains(self.ticker, self.selected_date)
|
||||
tradier_view.plot_oi(options, self.ticker, self.selected_date, parsed)
|
||||
else:
|
||||
options = yfinance_model.get_option_chain(self.ticker, self.selected_date)
|
||||
yfinance_view.plot_oi(
|
||||
options.calls, options.puts, self.ticker, self.selected_date, parsed
|
||||
)
|
||||
|
||||
def call_gr_hist(self, other_args: List[str]):
|
||||
syncretism_view.historical_greeks(self.ticker, self.selected_date, other_args)
|
||||
|
||||
|
||||
def menu(ticker: str):
|
||||
"""Options Menu."""
|
||||
|
||||
try:
|
||||
op_controller = OptionsController(ticker, stock)
|
||||
op_controller = OptionsController(ticker)
|
||||
op_controller.call_help(None)
|
||||
except IndexError:
|
||||
print("No options found for " + ticker)
|
||||
|
||||
412
gamestonk_terminal/options/op_helpers.py
Normal file
412
gamestonk_terminal/options/op_helpers.py
Normal file
@@ -0,0 +1,412 @@
|
||||
"""Option helper functions"""
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
from typing import List
|
||||
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
from gamestonk_terminal.helper_funcs import (
|
||||
parse_known_args_and_warn,
|
||||
check_non_negative,
|
||||
)
|
||||
|
||||
# pylint: disable=R1710
|
||||
|
||||
|
||||
def load(other_args: List[str]) -> str:
|
||||
"""Load ticker into object
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other_args: List[str]
|
||||
Agrparse arguments
|
||||
|
||||
Returns
|
||||
-------
|
||||
str:
|
||||
Ticker
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="opload",
|
||||
description="Load a ticker into option menu",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--ticker",
|
||||
action="store",
|
||||
dest="ticker",
|
||||
required="-h" not in other_args,
|
||||
help="Stock ticker",
|
||||
)
|
||||
|
||||
try:
|
||||
if other_args:
|
||||
if "-t" not in other_args and "-h" not in other_args:
|
||||
other_args.insert(0, "-t")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return ""
|
||||
print("")
|
||||
return ns_parser.ticker
|
||||
except Exception as e:
|
||||
print(e, "\n")
|
||||
return ""
|
||||
except SystemExit:
|
||||
print("")
|
||||
return ""
|
||||
|
||||
|
||||
# pylint: disable=no-else-return
|
||||
|
||||
|
||||
def select_option_date(avalaiable_dates: List[str], other_args: List[str]) -> str:
|
||||
"""Select an option date out of a supplied list
|
||||
|
||||
Parameters
|
||||
----------
|
||||
avalaiable_dates: List[str]
|
||||
Possible date options
|
||||
other_args: List[str]
|
||||
Arparse arguments
|
||||
Returns
|
||||
-------
|
||||
expiry_date: str
|
||||
Selected expiry date
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="exp",
|
||||
description="See and set expiration date",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--date",
|
||||
dest="n_date",
|
||||
action="store",
|
||||
type=int,
|
||||
default=-1,
|
||||
choices=range(len(avalaiable_dates)),
|
||||
help="Select index for expiry date.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-D",
|
||||
dest="date",
|
||||
type=str,
|
||||
choices=avalaiable_dates + [""],
|
||||
help="Select date (YYYY-MM-DD)",
|
||||
default="",
|
||||
)
|
||||
|
||||
try:
|
||||
if other_args:
|
||||
if "-" not in other_args[0]:
|
||||
other_args.insert(0, "-d")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return ""
|
||||
|
||||
# Print possible expiry dates
|
||||
if ns_parser.n_date == -1 and not ns_parser.date:
|
||||
print("\nAvailable expiry dates:")
|
||||
for i, d in enumerate(avalaiable_dates):
|
||||
print(f" {(2 - len(str(i))) * ' '}{i}. {d}")
|
||||
print("")
|
||||
return ""
|
||||
|
||||
# It means an expiry date was correctly selected
|
||||
else:
|
||||
if ns_parser.date:
|
||||
if ns_parser.date in avalaiable_dates:
|
||||
print(f"Expiraration set to {ns_parser.date} \n")
|
||||
return ns_parser.date
|
||||
else:
|
||||
print("Expiration not an option")
|
||||
return ""
|
||||
else:
|
||||
expiry_date = avalaiable_dates[ns_parser.n_date]
|
||||
print(f"Expiraration set to {expiry_date} \n")
|
||||
return expiry_date
|
||||
|
||||
except Exception as e:
|
||||
print(e, "\n")
|
||||
return ""
|
||||
|
||||
|
||||
def get_loss_at_strike(strike: float, chain: pd.DataFrame) -> float:
|
||||
"""Function to get the loss at the given expiry
|
||||
|
||||
Parameters
|
||||
----------
|
||||
strike: Union[int,float]
|
||||
Value to calculate total loss at
|
||||
chain: Dataframe:
|
||||
Dataframe containing at least strike and openInterest
|
||||
|
||||
Returns
|
||||
-------
|
||||
loss: Union[float,int]
|
||||
Total loss
|
||||
"""
|
||||
|
||||
itm_calls = chain[chain.index < strike][["OI_call"]]
|
||||
itm_calls["loss"] = (strike - itm_calls.index) * itm_calls["OI_call"]
|
||||
call_loss = itm_calls["loss"].sum()
|
||||
|
||||
itm_puts = chain[chain.index > strike][["OI_put"]]
|
||||
itm_puts["loss"] = (itm_puts.index - strike) * itm_puts["OI_put"]
|
||||
put_loss = itm_puts.loss.sum()
|
||||
loss = call_loss + put_loss
|
||||
|
||||
return loss
|
||||
|
||||
|
||||
def calculate_max_pain(chain: pd.DataFrame) -> int:
|
||||
"""Returns the max pain for a given call/put dataframe
|
||||
|
||||
Parameters
|
||||
----------
|
||||
chain: DataFrame
|
||||
Dataframe to calculate value from
|
||||
|
||||
Returns
|
||||
-------
|
||||
max_pain : int
|
||||
Max pain value
|
||||
"""
|
||||
|
||||
strikes = np.array(chain.index)
|
||||
if ("OI_call" not in chain.columns) or ("OI_put" not in chain.columns):
|
||||
print("Incorrect columns. Unable to parse max pain")
|
||||
return np.nan
|
||||
|
||||
loss = []
|
||||
for price_at_exp in strikes:
|
||||
loss.append(get_loss_at_strike(price_at_exp, chain))
|
||||
|
||||
chain["loss"] = loss
|
||||
max_pain = chain["loss"].idxmin()
|
||||
|
||||
return max_pain
|
||||
|
||||
|
||||
def vol(other_args: List[str]):
|
||||
"""Parse volume argparse
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other_args: List[str]
|
||||
Argparse arguments
|
||||
|
||||
Returns
|
||||
-------
|
||||
ns_parser: argparse.Namespace
|
||||
Parsed namespace
|
||||
"""
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="vol",
|
||||
description="Plot volume. Volume refers to the number of contracts traded today.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-m",
|
||||
"--min",
|
||||
default=-1,
|
||||
type=check_non_negative,
|
||||
help="Min strike to plot",
|
||||
dest="min",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-M",
|
||||
"--max",
|
||||
default=-1,
|
||||
type=check_non_negative,
|
||||
help="Max strike to plot",
|
||||
dest="max",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--calls",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest="calls",
|
||||
help="Flag to plot call options only",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--puts",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest="puts",
|
||||
help="Flag to plot put options only",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--source",
|
||||
type=str,
|
||||
default="tr",
|
||||
choices=["tr", "yf"],
|
||||
dest="source",
|
||||
help="Source to get data from",
|
||||
)
|
||||
|
||||
try:
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
return ns_parser
|
||||
|
||||
except Exception as e:
|
||||
print(e, "\n")
|
||||
|
||||
|
||||
def voi(other_args: List[str]):
|
||||
"""Parse Volume + open interest argparse
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other_args: List[str]
|
||||
Argparse arguments
|
||||
|
||||
Returns
|
||||
-------
|
||||
ns_parser: argparse.Namespace
|
||||
Parsed namespace
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="voi",
|
||||
description="""
|
||||
Plots Volume + Open Interest of calls vs puts.
|
||||
""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
"--minv",
|
||||
dest="min_vol",
|
||||
type=check_non_negative,
|
||||
default=-1,
|
||||
help="minimum volume (considering open interest) threshold of the plot.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-m",
|
||||
"--min",
|
||||
dest="min_sp",
|
||||
type=check_non_negative,
|
||||
default=-1,
|
||||
help="minimum strike price to consider in the plot.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-M",
|
||||
"--max",
|
||||
dest="max_sp",
|
||||
type=check_non_negative,
|
||||
default=-1,
|
||||
help="maximum strike price to consider in the plot.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--source",
|
||||
type=str,
|
||||
default="tr",
|
||||
choices=["tr", "yf"],
|
||||
dest="source",
|
||||
help="Source to get data from",
|
||||
)
|
||||
try:
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return None
|
||||
return ns_parser
|
||||
|
||||
except Exception as e:
|
||||
print(e, "\n")
|
||||
return None
|
||||
|
||||
|
||||
def oi(other_args: List[str]):
|
||||
"""Parse Open Interest argparse
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other_args: List[str]
|
||||
Argparse arguments
|
||||
|
||||
Returns
|
||||
-------
|
||||
ns_parser: argparse.Namespace
|
||||
Parsed namespace
|
||||
"""
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="oi",
|
||||
description="Plot open interest. Open interest represents the number of contracts that exist.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-m",
|
||||
"--min",
|
||||
default=-1,
|
||||
type=check_non_negative,
|
||||
help="Min strike to plot",
|
||||
dest="min",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-M",
|
||||
"--max",
|
||||
default=-1,
|
||||
type=check_non_negative,
|
||||
help="Max strike to plot",
|
||||
dest="max",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--calls",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest="calls",
|
||||
help="Flag to plot call options only",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--puts",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest="puts",
|
||||
help="Flag to plot put options only",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--source",
|
||||
type=str,
|
||||
default="tr",
|
||||
choices=["tr", "yf"],
|
||||
dest="source",
|
||||
help="Source to get data from",
|
||||
)
|
||||
|
||||
try:
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
|
||||
if not ns_parser:
|
||||
return None
|
||||
|
||||
return ns_parser
|
||||
|
||||
except Exception as e:
|
||||
print(e, "\n")
|
||||
return None
|
||||
@@ -5,10 +5,20 @@ import argparse
|
||||
import os
|
||||
from typing import List
|
||||
import configparser
|
||||
|
||||
import requests
|
||||
import pandas as pd
|
||||
import matplotlib.pyplot as plt
|
||||
from tabulate import tabulate
|
||||
from gamestonk_terminal.helper_funcs import parse_known_args_and_warn
|
||||
|
||||
from gamestonk_terminal.helper_funcs import (
|
||||
parse_known_args_and_warn,
|
||||
export_data,
|
||||
plot_autoscale,
|
||||
)
|
||||
from gamestonk_terminal.options import yfinance_model
|
||||
from gamestonk_terminal import config_plot as cfp
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
|
||||
presets_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), "presets/")
|
||||
|
||||
@@ -191,3 +201,197 @@ PC: Price Change; PB: Price-to-book. """,
|
||||
|
||||
except Exception as e:
|
||||
print(e, "\n")
|
||||
|
||||
|
||||
possible_greeks = [
|
||||
"iv",
|
||||
"gamma",
|
||||
"theta",
|
||||
"vega",
|
||||
"delta",
|
||||
"rho",
|
||||
"premium",
|
||||
]
|
||||
|
||||
|
||||
def check_valid_option_greek_header(headers: str) -> List[str]:
|
||||
"""Check valid greek selection
|
||||
|
||||
Parameters
|
||||
----------
|
||||
headers : str
|
||||
Option chains headers
|
||||
|
||||
Returns
|
||||
----------
|
||||
List[str]
|
||||
List of columns string
|
||||
"""
|
||||
columns = [str(item) for item in headers.split(",")]
|
||||
|
||||
for header in columns:
|
||||
if header not in possible_greeks:
|
||||
raise argparse.ArgumentTypeError("Invalid option chains header selected!")
|
||||
|
||||
return columns
|
||||
|
||||
|
||||
def historical_greeks(ticker: str, expiry: str, other_args: List[str]):
|
||||
"""Get historical greeks
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ticker: str
|
||||
Ticker
|
||||
expiry: str
|
||||
Expiration date
|
||||
other_args: List[str]
|
||||
Argparse arguments
|
||||
"""
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="gr_hist",
|
||||
description="Plot historical option greeks.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--strike",
|
||||
dest="strike",
|
||||
type=float,
|
||||
required="--chain" not in other_args or "-h" not in other_args,
|
||||
help="Strike price to look at",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--put",
|
||||
dest="put",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Flag for showing put option",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-g",
|
||||
"--greek",
|
||||
dest="greek",
|
||||
type=str,
|
||||
choices=possible_greeks,
|
||||
default="delta",
|
||||
help="Greek column to select",
|
||||
)
|
||||
|
||||
parser.add_argument("--chain", dest="chain_id", type=str, help="OCC option symbol")
|
||||
|
||||
parser.add_argument(
|
||||
"--raw", dest="raw", action="store_true", default=False, help="Display raw data"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
)
|
||||
|
||||
try:
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
if not ns_parser.chain_id:
|
||||
options = yfinance_model.get_option_chain(ticker, expiry)
|
||||
|
||||
if ns_parser.put:
|
||||
options = options.puts
|
||||
else:
|
||||
options = options.calls
|
||||
|
||||
chain_id = options.loc[
|
||||
options.strike == ns_parser.strike, "contractSymbol"
|
||||
].values[0]
|
||||
else:
|
||||
chain_id = ns_parser.chain_id
|
||||
|
||||
r = requests.get(f"https://api.syncretism.io/ops/historical/{chain_id}")
|
||||
|
||||
if r.status_code != 200:
|
||||
print("Error in request.")
|
||||
return
|
||||
|
||||
history = r.json()
|
||||
|
||||
iv, delta, gamma, theta, rho, vega, premium, price, time = (
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
)
|
||||
|
||||
for entry in history:
|
||||
|
||||
time.append(pd.to_datetime(entry["timestamp"], unit="s"))
|
||||
iv.append(entry["impliedVolatility"])
|
||||
gamma.append(entry["gamma"])
|
||||
delta.append(entry["delta"])
|
||||
theta.append(entry["theta"])
|
||||
rho.append(entry["rho"])
|
||||
vega.append(entry["vega"])
|
||||
premium.append(entry["premium"])
|
||||
price.append(entry["regularMarketPrice"])
|
||||
|
||||
data = {
|
||||
"iv": iv,
|
||||
"gamma": gamma,
|
||||
"delta": delta,
|
||||
"theta": theta,
|
||||
"rho": rho,
|
||||
"vega": vega,
|
||||
"premium": premium,
|
||||
"price": price,
|
||||
}
|
||||
|
||||
df = pd.DataFrame(data, index=time)
|
||||
|
||||
if ns_parser.raw:
|
||||
print(df.tail(20))
|
||||
if ns_parser.export:
|
||||
export_data(
|
||||
ns_parser.export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
f"historical_greek_{ticker}_{expiry}_{ns_parser.greek}_{str(ns_parser.strike).replace('.', 'p')}"
|
||||
f"_{['Call','Put'][ns_parser.put]}",
|
||||
df,
|
||||
)
|
||||
fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfp.PLOT_DPI)
|
||||
im1 = ax.plot(time, df[ns_parser.greek], c="firebrick", label=ns_parser.greek)
|
||||
ax.set_ylabel(ns_parser.greek)
|
||||
ax1 = ax.twinx()
|
||||
im2 = ax1.plot(time, price, c="dodgerblue", label="Stock Price")
|
||||
ax1.set_ylabel(f"{ticker} Price")
|
||||
ax1.set_xlabel("Date")
|
||||
ax.grid("on")
|
||||
ax.set_title(
|
||||
f"{ns_parser.greek} historical for {ticker.upper()} {ns_parser.strike} {['Call','Put'][ns_parser.put]}"
|
||||
)
|
||||
plt.gcf().autofmt_xdate()
|
||||
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
|
||||
ims = im1 + im2
|
||||
labels = [lab.get_label() for lab in ims]
|
||||
plt.legend(ims, labels, loc=0)
|
||||
fig.tight_layout(pad=1)
|
||||
plt.show()
|
||||
print("")
|
||||
|
||||
except Exception as e:
|
||||
print(e, "\n")
|
||||
|
||||
188
gamestonk_terminal/options/tradier_model.py
Normal file
188
gamestonk_terminal/options/tradier_model.py
Normal file
@@ -0,0 +1,188 @@
|
||||
"""Tradier options model"""
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import requests
|
||||
import pandas as pd
|
||||
|
||||
from gamestonk_terminal import config_terminal as cfg
|
||||
|
||||
option_columns = [
|
||||
"symbol",
|
||||
"bid",
|
||||
"ask",
|
||||
"strike",
|
||||
"bidsize",
|
||||
"asksize",
|
||||
"volume",
|
||||
"open_interest",
|
||||
"option_type",
|
||||
]
|
||||
greek_columns = ["delta", "gamma", "theta", "vega", "ask_iv", "bid_iv", "mid_iv"]
|
||||
df_columns = option_columns + greek_columns
|
||||
|
||||
default_columns = [
|
||||
"mid_iv",
|
||||
"vega",
|
||||
"delta",
|
||||
"gamma",
|
||||
"theta",
|
||||
"volume",
|
||||
"open_interest",
|
||||
"bid",
|
||||
"ask",
|
||||
]
|
||||
|
||||
# pylint: disable=no-else-return
|
||||
|
||||
|
||||
def option_expirations(ticker: str):
|
||||
"""Get available expiration dates for given ticker
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ticker: str
|
||||
Ticker to get expirations for
|
||||
|
||||
Returns
|
||||
-------
|
||||
dates: List[str]
|
||||
List of of available expirations
|
||||
"""
|
||||
r = requests.get(
|
||||
"https://sandbox.tradier.com/v1/markets/options/expirations",
|
||||
params={"symbol": ticker, "includeAllRoots": "true", "strikes": "false"},
|
||||
headers={
|
||||
"Authorization": f"Bearer {cfg.TRADIER_TOKEN}",
|
||||
"Accept": "application/json",
|
||||
},
|
||||
)
|
||||
if r.status_code == 200:
|
||||
dates = r.json()["expirations"]["date"]
|
||||
return dates
|
||||
else:
|
||||
print("Tradier request failed. Check token. \n")
|
||||
return []
|
||||
|
||||
|
||||
def get_option_chains(symbol: str, expiry: str) -> pd.DataFrame:
|
||||
"""Display option chains [Source: Tradier]"
|
||||
|
||||
Parameters
|
||||
----------
|
||||
symbol : str
|
||||
Ticker to get options for
|
||||
expiry : str
|
||||
Expiration date in the form of "YYYY-MM-DD"
|
||||
|
||||
Returns
|
||||
-------
|
||||
chains: pd.DataFrame
|
||||
Dataframe with options for the given Symbol and Expiration date
|
||||
"""
|
||||
params = {"symbol": symbol, "expiration": expiry, "greeks": "true"}
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {cfg.TRADIER_TOKEN}",
|
||||
"Accept": "application/json",
|
||||
}
|
||||
|
||||
response = requests.get(
|
||||
"https://sandbox.tradier.com/v1/markets/options/chains",
|
||||
params=params,
|
||||
headers=headers,
|
||||
)
|
||||
if response.status_code != 200:
|
||||
print("Error in request. Check TRADIER_TOKEN\n")
|
||||
return pd.DataFrame()
|
||||
|
||||
chains = process_chains(response)
|
||||
return chains
|
||||
|
||||
|
||||
def process_chains(response: requests.models.Response) -> pd.DataFrame:
|
||||
"""Function to take in the requests.get and return a DataFrame
|
||||
|
||||
Parameters
|
||||
----------
|
||||
response: requests.models.Response
|
||||
This is the response from tradier api.
|
||||
|
||||
Returns
|
||||
-------
|
||||
opt_chain: pd.DataFrame
|
||||
Dataframe with all available options
|
||||
"""
|
||||
json_response = response.json()
|
||||
options = json_response["options"]["option"]
|
||||
|
||||
opt_chain = pd.DataFrame(columns=df_columns)
|
||||
for idx, option in enumerate(options):
|
||||
data = [option[col] for col in option_columns]
|
||||
data += [option["greeks"][col] for col in greek_columns]
|
||||
opt_chain.loc[idx, :] = data
|
||||
|
||||
return opt_chain
|
||||
|
||||
|
||||
def last_price(ticker: str):
|
||||
"""Makes api request for last price
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ticker: str
|
||||
Ticker
|
||||
|
||||
Returns
|
||||
-------
|
||||
float:
|
||||
Last price
|
||||
"""
|
||||
r = requests.get(
|
||||
"https://sandbox.tradier.com/v1/markets/quotes",
|
||||
params={"symbols": ticker, "includeAllRoots": "true", "strikes": "false"},
|
||||
headers={
|
||||
"Authorization": f"Bearer {cfg.TRADIER_TOKEN}",
|
||||
"Accept": "application/json",
|
||||
},
|
||||
)
|
||||
if r.status_code == 200:
|
||||
return float(r.json()["quotes"]["quote"]["last"])
|
||||
else:
|
||||
print("Error getting last price")
|
||||
return None
|
||||
|
||||
|
||||
def historical_prices(symbol: str) -> pd.DataFrame:
|
||||
"""Get historical options prices
|
||||
|
||||
Parameters
|
||||
----------
|
||||
symbol: str
|
||||
OCC option chain symbol
|
||||
|
||||
Returns
|
||||
-------
|
||||
df_hist: pd.DataFrame
|
||||
Dataframe of historical options
|
||||
"""
|
||||
response = requests.get(
|
||||
"https://sandbox.tradier.com/v1/markets/history",
|
||||
params={"symbol": {symbol}, "interval": "daily"},
|
||||
headers={
|
||||
"Authorization": f"Bearer {cfg.TRADIER_TOKEN}",
|
||||
"Accept": "application/json",
|
||||
},
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
print("Error with request")
|
||||
return pd.DataFrame()
|
||||
|
||||
data = response.json()["history"]
|
||||
if not data:
|
||||
print("No historical data available")
|
||||
return pd.DataFrame()
|
||||
|
||||
df_hist = pd.DataFrame(data["day"]).set_index("date")
|
||||
|
||||
return df_hist
|
||||
@@ -1,43 +1,63 @@
|
||||
"""Helper functions for the options menu"""
|
||||
"""Tradier options view"""
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
from typing import List
|
||||
import requests
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from tabulate import tabulate
|
||||
from gamestonk_terminal.config_terminal import TRADIER_TOKEN
|
||||
from gamestonk_terminal.helper_funcs import (
|
||||
check_non_negative,
|
||||
parse_known_args_and_warn,
|
||||
)
|
||||
from bisect import bisect_left
|
||||
import os
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import matplotlib.pyplot as plt
|
||||
import seaborn as sns
|
||||
import mplfinance as mpf
|
||||
from colorama import Fore, Style
|
||||
from tabulate import tabulate
|
||||
|
||||
from gamestonk_terminal.helper_funcs import (
|
||||
parse_known_args_and_warn,
|
||||
check_non_negative,
|
||||
export_data,
|
||||
plot_autoscale,
|
||||
patch_pandas_text_adjustment,
|
||||
)
|
||||
from gamestonk_terminal.options import op_helpers, tradier_model
|
||||
from gamestonk_terminal import config_plot as cfp
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
|
||||
option_columns = [
|
||||
"bid",
|
||||
"ask",
|
||||
"strike",
|
||||
"bidsize",
|
||||
"asksize",
|
||||
"volume",
|
||||
"open_interest",
|
||||
"option_type",
|
||||
]
|
||||
greek_columns = ["delta", "gamma", "theta", "vega", "ask_iv", "bid_iv", "mid_iv"]
|
||||
df_columns = option_columns + greek_columns
|
||||
column_map = {"mid_iv": "iv", "open_interest": "oi", "volume": "vol"}
|
||||
|
||||
default_columns = [
|
||||
"mid_iv",
|
||||
"vega",
|
||||
"delta",
|
||||
"gamma",
|
||||
"theta",
|
||||
"volume",
|
||||
"open_interest",
|
||||
"bid",
|
||||
"ask",
|
||||
]
|
||||
|
||||
def red_highlight(val):
|
||||
"""Red highlight
|
||||
|
||||
Parameters
|
||||
----------
|
||||
val
|
||||
dataframe values to color
|
||||
|
||||
Returns
|
||||
----------
|
||||
str
|
||||
colored dataframes values
|
||||
"""
|
||||
return f"{Fore.RED}{val}{Style.RESET_ALL}"
|
||||
|
||||
|
||||
def green_highlight(val):
|
||||
"""Green highlight
|
||||
|
||||
Parameters
|
||||
----------
|
||||
values : List[str]
|
||||
dataframe values to color
|
||||
|
||||
Returns
|
||||
----------
|
||||
List[str]
|
||||
colored dataframes values
|
||||
"""
|
||||
return f"{Fore.GREEN}{val}{Style.RESET_ALL}"
|
||||
|
||||
|
||||
def check_valid_option_chains_headers(headers: str) -> List[str]:
|
||||
@@ -56,87 +76,29 @@ def check_valid_option_chains_headers(headers: str) -> List[str]:
|
||||
columns = [str(item) for item in headers.split(",")]
|
||||
|
||||
for header in columns:
|
||||
if header not in df_columns:
|
||||
if header not in tradier_model.df_columns:
|
||||
raise argparse.ArgumentTypeError("Invalid option chains header selected!")
|
||||
|
||||
return columns
|
||||
|
||||
|
||||
def process_chains(response: requests.models.Response) -> pd.DataFrame:
|
||||
"""Function to take in the requests.get and return a DataFrame
|
||||
def display_chains(ticker: str, expiry: str, other_args: List[str]):
|
||||
"""Display option chain
|
||||
|
||||
Parameters
|
||||
----------
|
||||
response: requests.models.Response
|
||||
This is the response from tradier api.
|
||||
|
||||
Returns
|
||||
-------
|
||||
opt_chain: pd.DataFrame
|
||||
Dataframe with all available options
|
||||
"""
|
||||
json_response = response.json()
|
||||
options = json_response["options"]["option"]
|
||||
|
||||
opt_chain = pd.DataFrame(columns=df_columns)
|
||||
for idx, option in enumerate(options):
|
||||
data = [option[col] for col in option_columns]
|
||||
data += [option["greeks"][col] for col in greek_columns]
|
||||
opt_chain.loc[idx, :] = data
|
||||
|
||||
return opt_chain
|
||||
|
||||
|
||||
def get_option_chains(symbol: str, expiry: str) -> pd.DataFrame:
|
||||
"""Display option chains [Source: Tradier]"
|
||||
|
||||
Parameters
|
||||
----------
|
||||
symbol : str
|
||||
Ticker to get options for
|
||||
expiry : str
|
||||
Expiration date in the form of "YYYY-MM-DD"
|
||||
|
||||
Returns
|
||||
-------
|
||||
pd.DataFrame
|
||||
Dataframe with options for the gievn Symbol and Expiration date
|
||||
"""
|
||||
params = {"symbol": symbol, "expiration": expiry, "greeks": "true"}
|
||||
|
||||
headers = {"Authorization": f"Bearer {TRADIER_TOKEN}", "Accept": "application/json"}
|
||||
|
||||
response = requests.get(
|
||||
"https://sandbox.tradier.com/v1/markets/options/chains",
|
||||
params=params,
|
||||
headers=headers,
|
||||
)
|
||||
if response.status_code != 200:
|
||||
print("Error in request. Check TRADIER_TOKEN\n")
|
||||
return pd.DataFrame()
|
||||
|
||||
chains = process_chains(response)
|
||||
return chains
|
||||
|
||||
|
||||
def display_chains(symbol: str, expiry: str, other_args: List[str]):
|
||||
"""Check valid option chains headers
|
||||
|
||||
Parameters
|
||||
----------
|
||||
headers : str
|
||||
Option chains headers
|
||||
|
||||
Returns
|
||||
----------
|
||||
List[str]
|
||||
List of columns string
|
||||
ticker: str
|
||||
Stock ticker
|
||||
expiry: str
|
||||
Expiration date of option
|
||||
other_args: List[str]
|
||||
Argparse arguments
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="chains",
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
description="Display option chains [Source: Tradier]",
|
||||
description="Display option chains",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--calls",
|
||||
@@ -172,22 +134,25 @@ def display_chains(symbol: str, expiry: str, other_args: List[str]):
|
||||
"-d",
|
||||
"--display",
|
||||
dest="to_display",
|
||||
default=default_columns,
|
||||
default=tradier_model.default_columns,
|
||||
type=check_valid_option_chains_headers,
|
||||
help="columns to look at. Columns can be: {bid, ask, strike, bidsize, asksize, volume, open_interest, delta, "
|
||||
"gamma, theta, vega, ask_iv, bid_iv, mid_iv} ",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
)
|
||||
|
||||
try:
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
chains_df = get_option_chains(symbol, expiry)
|
||||
|
||||
if chains_df.empty:
|
||||
return
|
||||
|
||||
chains_df = tradier_model.get_option_chains(ticker, expiry)
|
||||
columns = ns_parser.to_display + ["strike", "option_type"]
|
||||
chains_df = chains_df[columns].rename(columns=column_map)
|
||||
|
||||
@@ -205,6 +170,16 @@ def display_chains(symbol: str, expiry: str, other_args: List[str]):
|
||||
|
||||
chains_df = chains_df[chains_df["strike"] >= min_strike]
|
||||
chains_df = chains_df[chains_df["strike"] <= max_strike]
|
||||
|
||||
if ns_parser.export:
|
||||
# Note the extra dirname needed due to the subfolder in options
|
||||
export_data(
|
||||
ns_parser.export,
|
||||
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||||
f"chains_{ticker}_{expiry}",
|
||||
chains_df,
|
||||
)
|
||||
|
||||
calls_df = chains_df[chains_df.option_type == "call"].drop(
|
||||
columns=["option_type"]
|
||||
)
|
||||
@@ -238,6 +213,16 @@ def display_chains(symbol: str, expiry: str, other_args: List[str]):
|
||||
puts_df = puts_df[puts_df.columns[::-1]]
|
||||
chain_table = calls_df.merge(puts_df, on="strike")
|
||||
|
||||
if gtff.USE_COLOR:
|
||||
call_cols = [col for col in chain_table if col.endswith("_x")]
|
||||
put_cols = [col for col in chain_table if col.endswith("_y")]
|
||||
patch_pandas_text_adjustment()
|
||||
pd.set_option("display.max_colwidth", 0)
|
||||
pd.set_option("display.max_rows", None)
|
||||
for cc in call_cols:
|
||||
chain_table[cc] = chain_table[cc].astype(str).apply(green_highlight)
|
||||
for pc in put_cols:
|
||||
chain_table[pc] = chain_table[pc].astype(str).apply(red_highlight)
|
||||
headers = [
|
||||
col.strip("_x")
|
||||
if col.endswith("_x")
|
||||
@@ -255,7 +240,466 @@ def display_chains(symbol: str, expiry: str, other_args: List[str]):
|
||||
floatfmt=".2f",
|
||||
)
|
||||
)
|
||||
print("")
|
||||
print("")
|
||||
|
||||
except Exception as e:
|
||||
print(e, "\n")
|
||||
|
||||
|
||||
def plot_oi(
|
||||
options: pd.DataFrame,
|
||||
ticker: str,
|
||||
expiry: str,
|
||||
ns_parser: argparse.Namespace,
|
||||
):
|
||||
"""Plot open interest
|
||||
|
||||
Parameters
|
||||
----------
|
||||
options: pd.DataFrame
|
||||
Options dataframe with both calls and puts
|
||||
ticker: str
|
||||
Ticker
|
||||
expiry: str
|
||||
Expiry date for options
|
||||
ns_parser: argparse.Namespace
|
||||
Parsed namespace
|
||||
"""
|
||||
|
||||
current_price = tradier_model.last_price(ticker)
|
||||
|
||||
if ns_parser.min == -1:
|
||||
min_strike = 0.75 * current_price
|
||||
else:
|
||||
min_strike = ns_parser.min
|
||||
|
||||
if ns_parser.max == -1:
|
||||
max_strike = 1.25 * current_price
|
||||
else:
|
||||
max_strike = ns_parser.max
|
||||
|
||||
if ns_parser.calls and ns_parser.puts:
|
||||
print("Both flags selected, please select one", "\n")
|
||||
return
|
||||
|
||||
calls = options[options.option_type == "call"][["strike", "open_interest"]]
|
||||
puts = options[options.option_type == "put"][["strike", "open_interest"]]
|
||||
call_oi = calls.set_index("strike")["open_interest"] / 1000
|
||||
put_oi = puts.set_index("strike")["open_interest"] / 1000
|
||||
|
||||
df_opt = pd.merge(call_oi, put_oi, left_index=True, right_index=True)
|
||||
df_opt = df_opt.rename(
|
||||
columns={"open_interest_x": "OI_call", "open_interest_y": "OI_put"}
|
||||
)
|
||||
|
||||
max_pain = op_helpers.calculate_max_pain(df_opt)
|
||||
plt.style.use("classic")
|
||||
fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfp.PLOT_DPI)
|
||||
|
||||
if not ns_parser.calls:
|
||||
put_oi.plot(
|
||||
x="strike",
|
||||
y="open_interest",
|
||||
label="Puts",
|
||||
ax=ax,
|
||||
marker="o",
|
||||
ls="-",
|
||||
c="r",
|
||||
)
|
||||
if not ns_parser.puts:
|
||||
call_oi.plot(
|
||||
x="strike",
|
||||
y="open_interest",
|
||||
label="Calls",
|
||||
ax=ax,
|
||||
marker="o",
|
||||
ls="-",
|
||||
c="g",
|
||||
)
|
||||
ax.axvline(
|
||||
current_price, lw=2, c="k", ls="--", label="Current Price", alpha=0.7
|
||||
)
|
||||
ax.axvline(max_pain, lw=3, c="k", label=f"Max Pain: {max_pain}", alpha=0.7)
|
||||
ax.grid("on")
|
||||
ax.set_xlabel("Strike Price")
|
||||
ax.set_ylabel("Open Interest (1k) ")
|
||||
ax.set_xlim(min_strike, max_strike)
|
||||
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
|
||||
ax.set_title(f"Open Interest for {ticker.upper()} expiring {expiry}")
|
||||
plt.legend(loc=0)
|
||||
fig.tight_layout(pad=1)
|
||||
|
||||
plt.show()
|
||||
plt.style.use("default")
|
||||
print("")
|
||||
|
||||
|
||||
def plot_vol(
|
||||
options: pd.DataFrame,
|
||||
ticker: str,
|
||||
expiry: str,
|
||||
ns_parser: argparse.Namespace,
|
||||
):
|
||||
"""Plot volume
|
||||
|
||||
Parameters
|
||||
----------
|
||||
options: pd.DataFrame
|
||||
Options dataframe with both calls and puts
|
||||
ticker: str
|
||||
Ticker
|
||||
expiry: str
|
||||
Expiry date for options
|
||||
ns_parser: argparse.Namespace
|
||||
Parsed namespace
|
||||
"""
|
||||
|
||||
current_price = tradier_model.last_price(ticker)
|
||||
|
||||
if ns_parser.min == -1:
|
||||
min_strike = 0.75 * current_price
|
||||
else:
|
||||
min_strike = ns_parser.min
|
||||
|
||||
if ns_parser.max == -1:
|
||||
max_strike = 1.25 * current_price
|
||||
else:
|
||||
max_strike = ns_parser.max
|
||||
|
||||
if ns_parser.calls and ns_parser.puts:
|
||||
print("Both flags selected, please select one", "\n")
|
||||
return
|
||||
|
||||
calls = options[options.option_type == "call"][["strike", "volume"]]
|
||||
puts = options[options.option_type == "put"][["strike", "volume"]]
|
||||
call_v = calls.set_index("strike")["volume"] / 1000
|
||||
put_v = puts.set_index("strike")["volume"] / 1000
|
||||
plt.style.use("classic")
|
||||
fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfp.PLOT_DPI)
|
||||
|
||||
if not ns_parser.calls:
|
||||
put_v.plot(
|
||||
x="strike",
|
||||
y="volume",
|
||||
label="Puts",
|
||||
ax=ax,
|
||||
marker="o",
|
||||
ls="-",
|
||||
c="r",
|
||||
)
|
||||
if not ns_parser.puts:
|
||||
call_v.plot(
|
||||
x="strike",
|
||||
y="volume",
|
||||
label="Calls",
|
||||
ax=ax,
|
||||
marker="o",
|
||||
ls="-",
|
||||
c="g",
|
||||
)
|
||||
ax.axvline(current_price, lw=2, c="k", ls="--", label="Current Price", alpha=0.7)
|
||||
ax.grid("on")
|
||||
ax.set_xlabel("Strike Price")
|
||||
ax.set_ylabel("Volume (1k) ")
|
||||
ax.set_xlim(min_strike, max_strike)
|
||||
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
|
||||
ax.set_title(f"Volume for {ticker.upper()} expiring {expiry}")
|
||||
plt.legend(loc=0)
|
||||
fig.tight_layout(pad=1)
|
||||
|
||||
plt.show()
|
||||
plt.style.use("default")
|
||||
print("")
|
||||
|
||||
|
||||
def plot_volume_open_interest(
|
||||
ticker: str, exp_date: str, options: pd.DataFrame, ns_parser: argparse.Namespace
|
||||
):
|
||||
"""Plot volume and open interest
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ticker : str
|
||||
Main ticker to compare income
|
||||
exp_date : str
|
||||
Expiry date of the option
|
||||
Options: pd.DataFrame
|
||||
Options dataframe with both calls and puts
|
||||
ns_parser: argparse.Namespace
|
||||
Parsed namespace
|
||||
"""
|
||||
|
||||
current_price = tradier_model.last_price(ticker)
|
||||
calls = options[options.option_type == "call"][
|
||||
["strike", "volume", "open_interest"]
|
||||
]
|
||||
puts = options[options.option_type == "put"][["strike", "volume", "open_interest"]]
|
||||
|
||||
# Process Calls Data
|
||||
df_calls = calls.pivot_table(
|
||||
index="strike", values=["volume", "open_interest"], aggfunc="sum"
|
||||
).reindex()
|
||||
df_calls["strike"] = df_calls.index
|
||||
df_calls["type"] = "calls"
|
||||
df_calls["open_interest"] = df_calls["open_interest"]
|
||||
df_calls["volume"] = df_calls["volume"]
|
||||
df_calls["oi+v"] = df_calls["open_interest"] + df_calls["volume"]
|
||||
df_calls["spot"] = round(current_price, 2)
|
||||
|
||||
df_puts = puts.pivot_table(
|
||||
index="strike", values=["volume", "open_interest"], aggfunc="sum"
|
||||
).reindex()
|
||||
df_puts["strike"] = df_puts.index
|
||||
df_puts["type"] = "puts"
|
||||
df_puts["open_interest"] = df_puts["open_interest"]
|
||||
df_puts["volume"] = -df_puts["volume"]
|
||||
df_puts["open_interest"] = -df_puts["open_interest"]
|
||||
df_puts["oi+v"] = df_puts["open_interest"] + df_puts["volume"]
|
||||
df_puts["spot"] = round(current_price, 2)
|
||||
|
||||
call_oi = calls.set_index("strike")["open_interest"] / 1000
|
||||
put_oi = puts.set_index("strike")["open_interest"] / 1000
|
||||
|
||||
df_opt = pd.merge(call_oi, put_oi, left_index=True, right_index=True)
|
||||
df_opt = df_opt.rename(
|
||||
columns={"open_interest_x": "OI_call", "open_interest_y": "OI_put"}
|
||||
)
|
||||
|
||||
max_pain = op_helpers.calculate_max_pain(df_opt)
|
||||
|
||||
if ns_parser.min_vol == -1 and ns_parser.min_sp == -1 and ns_parser.max_sp == -1:
|
||||
# If no argument provided, we use the percentile 50 to get 50% of upper volume data
|
||||
volume_percentile_threshold = 50
|
||||
min_vol_calls = np.percentile(df_calls["oi+v"], volume_percentile_threshold)
|
||||
min_vol_puts = np.percentile(df_puts["oi+v"], volume_percentile_threshold)
|
||||
|
||||
df_calls = df_calls[df_calls["oi+v"] > min_vol_calls]
|
||||
df_puts = df_puts[df_puts["oi+v"] < min_vol_puts]
|
||||
|
||||
else:
|
||||
if ns_parser.min_vol > -1:
|
||||
df_calls = df_calls[df_calls["oi+v"] > ns_parser.min_vol]
|
||||
df_puts = df_puts[df_puts["oi+v"] < -ns_parser.min_vol]
|
||||
|
||||
if ns_parser.min_sp > -1:
|
||||
df_calls = df_calls[df_calls["strike"] > ns_parser.min_sp]
|
||||
df_puts = df_puts[df_puts["strike"] > ns_parser.min_sp]
|
||||
|
||||
if ns_parser.max_sp > -1:
|
||||
df_calls = df_calls[df_calls["strike"] < ns_parser.max_sp]
|
||||
df_puts = df_puts[df_puts["strike"] < ns_parser.max_sp]
|
||||
|
||||
if df_calls.empty and df_puts.empty:
|
||||
print(
|
||||
"The filtering applied is too strong, there is no data available for such conditions.\n"
|
||||
)
|
||||
return
|
||||
|
||||
# Initialize the matplotlib figure
|
||||
_, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfp.PLOT_DPI)
|
||||
|
||||
# make x axis symmetric
|
||||
axis_origin = max(abs(max(df_puts["oi+v"])), abs(max(df_calls["oi+v"])))
|
||||
ax.set_xlim(-axis_origin, +axis_origin)
|
||||
|
||||
sns.set_style(style="darkgrid")
|
||||
|
||||
g = sns.barplot(
|
||||
x="oi+v",
|
||||
y="strike",
|
||||
data=df_calls,
|
||||
label="Calls: Open Interest",
|
||||
color="lightgreen",
|
||||
orient="h",
|
||||
)
|
||||
|
||||
g = sns.barplot(
|
||||
x="volume",
|
||||
y="strike",
|
||||
data=df_calls,
|
||||
label="Calls: Volume",
|
||||
color="green",
|
||||
orient="h",
|
||||
)
|
||||
|
||||
g = sns.barplot(
|
||||
x="oi+v",
|
||||
y="strike",
|
||||
data=df_puts,
|
||||
label="Puts: Open Interest",
|
||||
color="pink",
|
||||
orient="h",
|
||||
)
|
||||
|
||||
g = sns.barplot(
|
||||
x="volume",
|
||||
y="strike",
|
||||
data=df_puts,
|
||||
label="Puts: Volume",
|
||||
color="red",
|
||||
orient="h",
|
||||
)
|
||||
|
||||
# draw spot line
|
||||
s = [float(strike.get_text()) for strike in ax.get_yticklabels()]
|
||||
spot_index = bisect_left(s, current_price) # find where the spot is on the graph
|
||||
spot_line = ax.axhline(spot_index, ls="--", color="dodgerblue", alpha=0.3)
|
||||
|
||||
# draw max pain line
|
||||
max_pain_index = bisect_left(s, max_pain)
|
||||
max_pain_line = ax.axhline(max_pain_index, ls="-", color="black", alpha=0.3)
|
||||
max_pain_line.set_linewidth(3)
|
||||
|
||||
# format ticklabels without - for puts
|
||||
g.set_xticks(g.get_xticks())
|
||||
xlabels = [f"{x:,.0f}".replace("-", "") for x in g.get_xticks()]
|
||||
g.set_xticklabels(xlabels)
|
||||
|
||||
plt.title(
|
||||
f"{ticker} volumes for {exp_date} (open interest displayed only during market hours)"
|
||||
)
|
||||
ax.invert_yaxis()
|
||||
|
||||
_ = ax.legend()
|
||||
handles, _ = ax.get_legend_handles_labels()
|
||||
handles.append(spot_line)
|
||||
handles.append(max_pain_line)
|
||||
|
||||
# create legend labels + add to graph
|
||||
labels = [
|
||||
"Calls open interest",
|
||||
"Calls volume ",
|
||||
"Puts open interest",
|
||||
"Puts volume",
|
||||
"Current stock price",
|
||||
f"Max pain = {max_pain}",
|
||||
]
|
||||
|
||||
plt.legend(handles=handles[:], labels=labels)
|
||||
sns.despine(left=True, bottom=True)
|
||||
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
plt.show()
|
||||
plt.style.use("default")
|
||||
print("")
|
||||
|
||||
|
||||
def display_historical(ticker: str, expiry: str, other_args: List[str]):
|
||||
"""Plot historical option data
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ticker: str
|
||||
Ticker
|
||||
expiry: str
|
||||
Expiration of option
|
||||
other_args: List[str]
|
||||
Argparse arguments
|
||||
"""
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="hist",
|
||||
description="Gets historical quotes for given option chain",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--strike",
|
||||
dest="strike",
|
||||
type=float,
|
||||
required="--chain" not in other_args or "-h" not in other_args,
|
||||
help="Strike price to look at",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--put",
|
||||
dest="put",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Flag for showing put option",
|
||||
)
|
||||
parser.add_argument("--chain", dest="chain_id", type=str, help="OCC option symbol")
|
||||
|
||||
parser.add_argument(
|
||||
"--raw", dest="raw", action="store_true", default=False, help="Display raw data"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
)
|
||||
|
||||
try:
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
if not ns_parser.chain_id:
|
||||
strike = float(ns_parser.strike)
|
||||
op_type = ["call", "put"][ns_parser.put]
|
||||
chain = tradier_model.get_option_chains(ticker, expiry)
|
||||
try:
|
||||
symbol = chain[
|
||||
(chain.strike == strike) & (chain.option_type == op_type)
|
||||
]["symbol"].values[0]
|
||||
except IndexError:
|
||||
print(f"Strike: {strike}, Option type: {op_type} not not found \n")
|
||||
return
|
||||
else:
|
||||
symbol = ns_parser.chain_id
|
||||
|
||||
df_hist = tradier_model.historical_prices(symbol)
|
||||
df_hist.index = pd.DatetimeIndex(df_hist.index)
|
||||
|
||||
if ns_parser.raw:
|
||||
print(df_hist)
|
||||
|
||||
mc = mpf.make_marketcolors(
|
||||
up="green", down="red", edge="black", wick="black", volume="in", ohlc="i"
|
||||
)
|
||||
|
||||
s = mpf.make_mpf_style(marketcolors=mc, gridstyle=":", y_on_right=True)
|
||||
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
|
||||
mpf.plot(
|
||||
df_hist,
|
||||
type="candle",
|
||||
volume=True,
|
||||
title=f"\n{ticker.upper()} {strike} {op_type} expiring {expiry} Historical",
|
||||
style=s,
|
||||
figratio=(10, 7),
|
||||
figscale=1.10,
|
||||
figsize=(plot_autoscale()),
|
||||
update_width_config=dict(
|
||||
candle_linewidth=1.0, candle_width=0.8, volume_linewidth=1.0
|
||||
),
|
||||
)
|
||||
|
||||
print("")
|
||||
|
||||
if ns_parser.export:
|
||||
export_data(
|
||||
ns_parser.export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
f"historical_op_{ticker}_{expiry}_{str(strike).replace('.','p')}_{op_type}",
|
||||
df_hist,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
print(e, "\n")
|
||||
|
||||
except SystemExit:
|
||||
print("")
|
||||
|
||||
@@ -1,517 +0,0 @@
|
||||
""" Volume view """
|
||||
__docformat__ = "numpy"
|
||||
|
||||
from bisect import bisect_left
|
||||
from typing import List
|
||||
import argparse
|
||||
import pandas as pd
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import seaborn as sns
|
||||
|
||||
from gamestonk_terminal.helper_funcs import (
|
||||
plot_autoscale,
|
||||
check_non_negative,
|
||||
parse_known_args_and_warn,
|
||||
)
|
||||
from gamestonk_terminal import config_plot as cfgPlot
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
|
||||
|
||||
def plot_volume_open_interest(
|
||||
other_args: List[str],
|
||||
ticker: str,
|
||||
exp_date: str,
|
||||
last_adj_close_price: float,
|
||||
op_calls: pd.DataFrame,
|
||||
op_puts: pd.DataFrame,
|
||||
):
|
||||
"""Plot volume open interest
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other_args : List[str]
|
||||
Command line arguments to be processed with argparse
|
||||
ticker : str
|
||||
Main ticker to compare income
|
||||
exp_date : str
|
||||
Expiry date of the option
|
||||
last_adj_close_price: float
|
||||
Last adjusted closing price
|
||||
op_calls: pd.DataFrame
|
||||
Option data calls
|
||||
op_puts: pd.DataFrame
|
||||
Option data puts
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="voi",
|
||||
description="""
|
||||
Plots Volume + Open Interest of calls vs puts. [Source: Yahoo Finance]
|
||||
""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
"--minv",
|
||||
dest="min_vol",
|
||||
type=check_non_negative,
|
||||
default=-1,
|
||||
help="minimum volume (considering open interest) threshold of the plot.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-m",
|
||||
"--min",
|
||||
dest="min_sp",
|
||||
type=check_non_negative,
|
||||
default=-1,
|
||||
help="minimum strike price to consider in the plot.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-M",
|
||||
"--max",
|
||||
dest="max_sp",
|
||||
type=check_non_negative,
|
||||
default=-1,
|
||||
help="maximum strike price to consider in the plot.",
|
||||
)
|
||||
|
||||
try:
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
df_calls, df_puts, max_pain = get_calls_puts_maxpain(
|
||||
op_calls, op_puts, last_adj_close_price
|
||||
)
|
||||
|
||||
if (
|
||||
ns_parser.min_vol == -1
|
||||
and ns_parser.min_sp == -1
|
||||
and ns_parser.max_sp == -1
|
||||
):
|
||||
# If no argument provided, we use the percentile 50 to get 50% of upper volume data
|
||||
volume_percentile_threshold = 50
|
||||
min_vol_calls = np.percentile(df_calls["oi+v"], volume_percentile_threshold)
|
||||
min_vol_puts = np.percentile(df_puts["oi+v"], volume_percentile_threshold)
|
||||
|
||||
df_calls = df_calls[df_calls["oi+v"] > min_vol_calls]
|
||||
df_puts = df_puts[df_puts["oi+v"] < min_vol_puts]
|
||||
|
||||
else:
|
||||
if ns_parser.min_vol > -1:
|
||||
df_calls = df_calls[df_calls["oi+v"] > ns_parser.min_vol]
|
||||
df_puts = df_puts[df_puts["oi+v"] < -ns_parser.min_vol]
|
||||
|
||||
if ns_parser.min_sp > -1:
|
||||
df_calls = df_calls[df_calls["strike"] > ns_parser.min_sp]
|
||||
df_puts = df_puts[df_puts["strike"] > ns_parser.min_sp]
|
||||
|
||||
if ns_parser.max_sp > -1:
|
||||
df_calls = df_calls[df_calls["strike"] < ns_parser.max_sp]
|
||||
df_puts = df_puts[df_puts["strike"] < ns_parser.max_sp]
|
||||
|
||||
if df_calls.empty and df_puts.empty:
|
||||
print(
|
||||
"The filtering applied is too strong, there is no data available for such conditions.\n"
|
||||
)
|
||||
return
|
||||
|
||||
# Initialize the matplotlib figure
|
||||
_, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfgPlot.PLOT_DPI)
|
||||
|
||||
# make x axis symmetric
|
||||
axis_origin = max(abs(max(df_puts["oi+v"])), abs(max(df_calls["oi+v"])))
|
||||
ax.set_xlim(-axis_origin, +axis_origin)
|
||||
|
||||
sns.set_style(style="darkgrid")
|
||||
|
||||
g = sns.barplot(
|
||||
x="oi+v",
|
||||
y="strike",
|
||||
data=df_calls,
|
||||
label="Calls: Open Interest",
|
||||
color="lightgreen",
|
||||
orient="h",
|
||||
)
|
||||
|
||||
g = sns.barplot(
|
||||
x="volume",
|
||||
y="strike",
|
||||
data=df_calls,
|
||||
label="Calls: Volume",
|
||||
color="green",
|
||||
orient="h",
|
||||
)
|
||||
|
||||
g = sns.barplot(
|
||||
x="oi+v",
|
||||
y="strike",
|
||||
data=df_puts,
|
||||
label="Puts: Open Interest",
|
||||
color="pink",
|
||||
orient="h",
|
||||
)
|
||||
|
||||
g = sns.barplot(
|
||||
x="volume",
|
||||
y="strike",
|
||||
data=df_puts,
|
||||
label="Puts: Volume",
|
||||
color="red",
|
||||
orient="h",
|
||||
)
|
||||
|
||||
# draw spot line
|
||||
s = [float(strike.get_text()) for strike in ax.get_yticklabels()]
|
||||
spot_index = bisect_left(
|
||||
s, last_adj_close_price
|
||||
) # find where the spot is on the graph
|
||||
spot_line = ax.axhline(spot_index, ls="--", color="dodgerblue", alpha=0.3)
|
||||
|
||||
# draw max pain line
|
||||
max_pain_index = bisect_left(s, max_pain)
|
||||
max_pain_line = ax.axhline(max_pain_index, ls="-", color="black", alpha=0.3)
|
||||
max_pain_line.set_linewidth(5)
|
||||
|
||||
# format ticklabels without - for puts
|
||||
g.set_xticks(g.get_xticks())
|
||||
xlabels = [f"{x:,.0f}".replace("-", "") for x in g.get_xticks()]
|
||||
g.set_xticklabels(xlabels)
|
||||
|
||||
plt.title(
|
||||
f"{ticker} volumes for {exp_date} (open interest displayed only during market hours)"
|
||||
)
|
||||
ax.invert_yaxis()
|
||||
|
||||
_ = ax.legend()
|
||||
handles, _ = ax.get_legend_handles_labels()
|
||||
handles.append(spot_line)
|
||||
handles.append(max_pain_line)
|
||||
|
||||
# create legend labels + add to graph
|
||||
labels = [
|
||||
"Calls open interest",
|
||||
"Calls volume ",
|
||||
"Puts open interest",
|
||||
"Puts volume",
|
||||
"Current stock price",
|
||||
f"Max pain = {max_pain}",
|
||||
]
|
||||
|
||||
plt.legend(handles=handles[:], labels=labels)
|
||||
sns.despine(left=True, bottom=True)
|
||||
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
plt.show()
|
||||
print("")
|
||||
|
||||
except Exception as e:
|
||||
print(e, "\n")
|
||||
|
||||
|
||||
def get_calls_puts_maxpain(
|
||||
op_calls: pd.DataFrame, op_puts: pd.DataFrame, last_adj_close_price: float
|
||||
):
|
||||
"""Get calls and puts dataframes, and max pain
|
||||
|
||||
Parameters
|
||||
----------
|
||||
op_calls: pd.DataFrame
|
||||
Option data calls
|
||||
op_puts: pd.DataFrame
|
||||
Option data puts
|
||||
last_adj_close_price: float
|
||||
Last adjusted closing price
|
||||
|
||||
Returns
|
||||
----------
|
||||
pd.DataFrame
|
||||
Processed calls dataframe
|
||||
pd.DataFrame
|
||||
Processed puts dataframe
|
||||
float
|
||||
Max pain
|
||||
"""
|
||||
df_calls = pd.DataFrame()
|
||||
df_puts = pd.DataFrame()
|
||||
max_pain = 0
|
||||
|
||||
if not op_calls.empty:
|
||||
# Process Calls Data
|
||||
df_calls = op_calls.pivot_table(
|
||||
index="strike", values=["volume", "openInterest"], aggfunc="sum"
|
||||
).reindex()
|
||||
df_calls["strike"] = df_calls.index
|
||||
df_calls["type"] = "calls"
|
||||
df_calls["openInterest"] = df_calls["openInterest"]
|
||||
df_calls["volume"] = df_calls["volume"]
|
||||
df_calls["oi+v"] = df_calls["openInterest"] + df_calls["volume"]
|
||||
df_calls["spot"] = round(last_adj_close_price, 2)
|
||||
|
||||
if not op_puts.empty:
|
||||
# Process Puts Data
|
||||
df_puts = op_puts.pivot_table(
|
||||
index="strike", values=["volume", "openInterest"], aggfunc="sum"
|
||||
).reindex()
|
||||
df_puts["strike"] = df_puts.index
|
||||
df_puts["type"] = "puts"
|
||||
df_puts["openInterest"] = df_puts["openInterest"]
|
||||
df_puts["volume"] = -df_puts["volume"]
|
||||
df_puts["openInterest"] = -df_puts["openInterest"]
|
||||
df_puts["oi+v"] = df_puts["openInterest"] + df_puts["volume"]
|
||||
df_puts["spot"] = round(last_adj_close_price, 2)
|
||||
|
||||
if not op_calls.empty and not op_puts.empty:
|
||||
# Get max pain
|
||||
df_opt = pd.merge(df_calls, df_puts, left_index=True, right_index=True)
|
||||
df_opt = df_opt[["openInterest_x", "openInterest_y"]].rename(
|
||||
columns={"openInterest_x": "OI_call", "openInterest_y": "OI_put"}
|
||||
)
|
||||
max_pain = get_max_pain(df_opt)
|
||||
|
||||
return df_calls, df_puts, max_pain
|
||||
|
||||
|
||||
def plot_calls_volume_open_interest(
|
||||
other_args: List[str],
|
||||
ticker: str,
|
||||
exp_date: str,
|
||||
last_adj_close_price: float,
|
||||
op_calls: pd.DataFrame,
|
||||
):
|
||||
"""Plot calls volume open interest
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other_args : List[str]
|
||||
Command line arguments to be processed with argparse
|
||||
ticker : str
|
||||
Main ticker to compare income
|
||||
exp_date : str
|
||||
Expiry date of the option
|
||||
last_adj_close_price: float
|
||||
Last adjusted closing price
|
||||
op_calls: pd.DataFrame
|
||||
Option data calls
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="vcalls",
|
||||
description="""
|
||||
Plots Calls Volume + Open Interest. [Source: Yahoo Finance]
|
||||
""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-m",
|
||||
"--min",
|
||||
dest="min_sp",
|
||||
type=check_non_negative,
|
||||
default=-1,
|
||||
help="minimum strike price to consider in the plot.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-M",
|
||||
"--max",
|
||||
dest="max_sp",
|
||||
type=check_non_negative,
|
||||
default=-1,
|
||||
help="maximum strike price to consider in the plot.",
|
||||
)
|
||||
|
||||
try:
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
df_calls, _, _ = get_calls_puts_maxpain(
|
||||
op_calls, pd.DataFrame(), last_adj_close_price
|
||||
)
|
||||
|
||||
if ns_parser.min_sp == -1:
|
||||
min_strike = 0.75 * last_adj_close_price
|
||||
else:
|
||||
min_strike = ns_parser.min_sp
|
||||
|
||||
if ns_parser.max_sp == -1:
|
||||
max_strike = 1.25 * last_adj_close_price
|
||||
else:
|
||||
max_strike = ns_parser.max_sp
|
||||
|
||||
plt.figure(figsize=plot_autoscale(), dpi=cfgPlot.PLOT_DPI)
|
||||
|
||||
plt.axvline(last_adj_close_price, lw=2)
|
||||
plt.bar(
|
||||
df_calls["strike"],
|
||||
df_calls["volume"] + df_calls["openInterest"],
|
||||
color="lightgreen",
|
||||
width=0.45,
|
||||
)
|
||||
plt.bar(df_calls["strike"], df_calls["volume"], color="green", width=0.45)
|
||||
|
||||
plt.title(f"{ticker} calls volumes for {exp_date} ")
|
||||
plt.legend(["Stock Price", "Open Interest", "Volume"])
|
||||
plt.xlabel("Strike Price")
|
||||
plt.ylabel("Volume")
|
||||
plt.xlim([min_strike, max_strike])
|
||||
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
plt.show()
|
||||
|
||||
print("")
|
||||
|
||||
except Exception as e:
|
||||
print(e, "\n")
|
||||
|
||||
|
||||
def plot_puts_volume_open_interest(
|
||||
other_args: List[str],
|
||||
ticker: str,
|
||||
exp_date: str,
|
||||
last_adj_close_price: float,
|
||||
op_puts: pd.DataFrame,
|
||||
):
|
||||
"""Plot puts volume open interest
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other_args : List[str]
|
||||
Command line arguments to be processed with argparse
|
||||
ticker : str
|
||||
Main ticker to compare income
|
||||
exp_date : str
|
||||
Expiry date of the option
|
||||
last_adj_close_price: float
|
||||
Last adjusted closing price
|
||||
op_puts: pd.DataFrame
|
||||
Option data puts
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="vputs",
|
||||
description="""
|
||||
Plots Puts Volume + Open Interest. [Source: Yahoo Finance]
|
||||
""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-m",
|
||||
"--min",
|
||||
dest="min_sp",
|
||||
type=check_non_negative,
|
||||
default=-1,
|
||||
help="minimum strike price to consider in the plot.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-M",
|
||||
"--max",
|
||||
dest="max_sp",
|
||||
type=check_non_negative,
|
||||
default=-1,
|
||||
help="maximum strike price to consider in the plot.",
|
||||
)
|
||||
|
||||
try:
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
_, df_puts, _ = get_calls_puts_maxpain(
|
||||
pd.DataFrame(), op_puts, last_adj_close_price
|
||||
)
|
||||
|
||||
if ns_parser.min_sp == -1:
|
||||
min_strike = 0.75 * last_adj_close_price
|
||||
else:
|
||||
min_strike = ns_parser.min_sp
|
||||
|
||||
if ns_parser.max_sp == -1:
|
||||
max_strike = 1.25 * last_adj_close_price
|
||||
else:
|
||||
max_strike = ns_parser.max_sp
|
||||
|
||||
plt.figure(figsize=plot_autoscale(), dpi=cfgPlot.PLOT_DPI)
|
||||
|
||||
plt.axvline(last_adj_close_price, lw=2)
|
||||
plt.bar(
|
||||
df_puts["strike"],
|
||||
-df_puts["volume"] - df_puts["openInterest"],
|
||||
color="pink",
|
||||
width=0.45,
|
||||
)
|
||||
plt.bar(df_puts["strike"], -df_puts["volume"], color="red", width=0.45)
|
||||
|
||||
plt.title(f"{ticker} puts volumes for {exp_date} ")
|
||||
plt.legend(["Stock Price", "Open Interest", "Volume"])
|
||||
plt.xlabel("Strike Price")
|
||||
plt.ylabel("Volume")
|
||||
plt.xlim([min_strike, max_strike])
|
||||
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
plt.show()
|
||||
|
||||
print("")
|
||||
|
||||
except Exception as e:
|
||||
print(e, "\n")
|
||||
|
||||
|
||||
def get_loss_at_strike(strike: float, chain: pd.DataFrame) -> float:
|
||||
"""Function to get the loss at the given expiry
|
||||
|
||||
Parameters
|
||||
----------
|
||||
strike: Union[int,float]
|
||||
Value to calculate total loss at
|
||||
chain: Dataframe:
|
||||
Dataframe containing at least strike and openInterest
|
||||
|
||||
Returns
|
||||
-------
|
||||
loss: Union[float,int]
|
||||
Total loss
|
||||
"""
|
||||
|
||||
itm_calls = chain[chain.index < strike][["OI_call"]]
|
||||
itm_calls["loss"] = (strike - itm_calls.index) * itm_calls["OI_call"]
|
||||
call_loss = itm_calls["loss"].sum()
|
||||
# The *-1 below is due to a sign change for plotting in the _view code
|
||||
itm_puts = chain[chain.index > strike][["OI_put"]]
|
||||
itm_puts["loss"] = (itm_puts.index - strike) * itm_puts["OI_put"] * -1
|
||||
put_loss = itm_puts.loss.sum()
|
||||
loss = call_loss + put_loss
|
||||
|
||||
return loss
|
||||
|
||||
|
||||
def get_max_pain(chain: pd.DataFrame) -> int:
|
||||
"""Returns the max pain for a given call/put dataframe
|
||||
|
||||
Parameters
|
||||
----------
|
||||
chain: DataFrame
|
||||
Dataframe to calculate value from
|
||||
|
||||
Returns
|
||||
-------
|
||||
max_pain : int
|
||||
Max pain value
|
||||
"""
|
||||
|
||||
strikes = np.array(chain.index)
|
||||
if ("OI_call" not in chain.columns) or ("OI_put" not in chain.columns):
|
||||
print("Incorrect columns. Unable to parse max pain")
|
||||
return np.nan
|
||||
|
||||
loss = []
|
||||
for price_at_exp in strikes:
|
||||
loss.append(get_loss_at_strike(price_at_exp, chain))
|
||||
|
||||
chain["loss"] = loss
|
||||
max_pain = chain["loss"].idxmin()
|
||||
|
||||
return max_pain
|
||||
42
gamestonk_terminal/options/yfinance_model.py
Normal file
42
gamestonk_terminal/options/yfinance_model.py
Normal file
@@ -0,0 +1,42 @@
|
||||
"""Yfinance options model"""
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import yfinance as yf
|
||||
|
||||
|
||||
def option_expirations(ticker: str):
|
||||
"""Get available expiration dates for given ticker
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ticker: str
|
||||
Ticker to get expirations for
|
||||
|
||||
Returns
|
||||
-------
|
||||
dates: List[str]
|
||||
List of of available expirations
|
||||
"""
|
||||
yf_ticker = yf.Ticker(ticker)
|
||||
dates = list(yf_ticker.options)
|
||||
return dates
|
||||
|
||||
|
||||
def get_option_chain(ticker: str, expiration: str):
|
||||
"""Gets option chain from yf for given ticker and expiration
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ticker: str
|
||||
Ticker to get options for
|
||||
expiration: str
|
||||
Date to get options for
|
||||
|
||||
Returns
|
||||
-------
|
||||
chains: yf.ticker.Options
|
||||
Options chain
|
||||
"""
|
||||
yf_ticker = yf.Ticker(ticker)
|
||||
chains = yf_ticker.option_chain(expiration)
|
||||
return chains
|
||||
367
gamestonk_terminal/options/yfinance_view.py
Normal file
367
gamestonk_terminal/options/yfinance_view.py
Normal file
@@ -0,0 +1,367 @@
|
||||
"""Yfinance options view"""
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
from bisect import bisect_left
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import seaborn as sns
|
||||
import pandas as pd
|
||||
import yfinance as yf
|
||||
import numpy as np
|
||||
|
||||
import gamestonk_terminal.feature_flags as gtff
|
||||
import gamestonk_terminal.config_plot as cfp
|
||||
from gamestonk_terminal.helper_funcs import (
|
||||
plot_autoscale,
|
||||
)
|
||||
from gamestonk_terminal.options import op_helpers
|
||||
|
||||
|
||||
def plot_oi(
|
||||
calls: pd.DataFrame,
|
||||
puts: pd.DataFrame,
|
||||
ticker: str,
|
||||
expiry: str,
|
||||
ns_parser: argparse.Namespace,
|
||||
):
|
||||
"""Plot open interest
|
||||
|
||||
Parameters
|
||||
----------
|
||||
calls: pd.DataFrame
|
||||
Dataframe of call options
|
||||
puts: pd.DataFrame
|
||||
Dataframe of put options
|
||||
ticker: str
|
||||
Ticker
|
||||
expiry: str
|
||||
Expiry date for options
|
||||
ns_parser: argparse.Namespace
|
||||
Parsed namespace
|
||||
"""
|
||||
|
||||
current_price = float(yf.Ticker(ticker).info["regularMarketPrice"])
|
||||
|
||||
if ns_parser.min == -1:
|
||||
min_strike = 0.75 * current_price
|
||||
else:
|
||||
min_strike = ns_parser.min
|
||||
|
||||
if ns_parser.max == -1:
|
||||
max_strike = 1.25 * current_price
|
||||
else:
|
||||
max_strike = ns_parser.max
|
||||
|
||||
if ns_parser.calls and ns_parser.puts:
|
||||
print("Both flags selected, please select one", "\n")
|
||||
return
|
||||
|
||||
call_oi = calls.set_index("strike")["openInterest"] / 1000
|
||||
put_oi = puts.set_index("strike")["openInterest"] / 1000
|
||||
|
||||
df_opt = pd.merge(call_oi, put_oi, left_index=True, right_index=True)
|
||||
df_opt = df_opt.rename(
|
||||
columns={"openInterest_x": "OI_call", "openInterest_y": "OI_put"}
|
||||
)
|
||||
|
||||
max_pain = op_helpers.calculate_max_pain(df_opt)
|
||||
plt.style.use("classic")
|
||||
fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfp.PLOT_DPI)
|
||||
|
||||
if not ns_parser.calls:
|
||||
put_oi.plot(
|
||||
x="strike",
|
||||
y="openInterest",
|
||||
label="Puts",
|
||||
ax=ax,
|
||||
marker="o",
|
||||
ls="-",
|
||||
c="r",
|
||||
)
|
||||
if not ns_parser.puts:
|
||||
call_oi.plot(
|
||||
x="strike",
|
||||
y="openInterest",
|
||||
label="Calls",
|
||||
ax=ax,
|
||||
marker="o",
|
||||
ls="-",
|
||||
c="g",
|
||||
)
|
||||
ax.axvline(
|
||||
current_price, lw=2, c="k", ls="--", label="Current Price", alpha=0.7
|
||||
)
|
||||
ax.axvline(max_pain, lw=3, c="k", label=f"Max Pain: {max_pain}", alpha=0.7)
|
||||
ax.grid("on")
|
||||
ax.set_xlabel("Strike Price")
|
||||
ax.set_ylabel("Open Interest (1k) ")
|
||||
ax.set_xlim(min_strike, max_strike)
|
||||
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
|
||||
ax.set_title(f"Open Interest for {ticker.upper()} expiring {expiry}")
|
||||
plt.legend(loc=0)
|
||||
fig.tight_layout(pad=1)
|
||||
|
||||
plt.show()
|
||||
plt.style.use("default")
|
||||
print("")
|
||||
|
||||
|
||||
def plot_vol(
|
||||
calls: pd.DataFrame,
|
||||
puts: pd.DataFrame,
|
||||
ticker: str,
|
||||
expiry: str,
|
||||
ns_parser: argparse.Namespace,
|
||||
):
|
||||
"""Plot volume
|
||||
|
||||
Parameters
|
||||
----------
|
||||
calls: pd.DataFrame
|
||||
Dataframe of call options
|
||||
puts: pd.DataFrame
|
||||
Dataframe of put options
|
||||
ticker: str
|
||||
Ticker
|
||||
expiry: str
|
||||
Expiry date for options
|
||||
ns_parser: argparse.Namespace
|
||||
Parsed namespace
|
||||
"""
|
||||
|
||||
current_price = float(yf.Ticker(ticker).info["regularMarketPrice"])
|
||||
|
||||
if ns_parser.min == -1:
|
||||
min_strike = 0.75 * current_price
|
||||
else:
|
||||
min_strike = ns_parser.min
|
||||
|
||||
if ns_parser.max == -1:
|
||||
max_strike = 1.25 * current_price
|
||||
else:
|
||||
max_strike = ns_parser.max
|
||||
|
||||
if ns_parser.calls and ns_parser.puts:
|
||||
print("Both flags selected, please select one", "\n")
|
||||
return
|
||||
|
||||
call_v = calls.set_index("strike")["volume"] / 1000
|
||||
put_v = puts.set_index("strike")["volume"] / 1000
|
||||
plt.style.use("classic")
|
||||
fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfp.PLOT_DPI)
|
||||
|
||||
if not ns_parser.calls:
|
||||
put_v.plot(
|
||||
x="strike",
|
||||
y="volume",
|
||||
label="Puts",
|
||||
ax=ax,
|
||||
marker="o",
|
||||
ls="-",
|
||||
c="r",
|
||||
)
|
||||
if not ns_parser.puts:
|
||||
call_v.plot(
|
||||
x="strike",
|
||||
y="volume",
|
||||
label="Calls",
|
||||
ax=ax,
|
||||
marker="o",
|
||||
ls="-",
|
||||
c="g",
|
||||
)
|
||||
ax.axvline(current_price, lw=2, c="k", ls="--", label="Current Price", alpha=0.7)
|
||||
ax.grid("on")
|
||||
ax.set_xlabel("Strike Price")
|
||||
ax.set_ylabel("Volume (1k) ")
|
||||
ax.set_xlim(min_strike, max_strike)
|
||||
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
|
||||
ax.set_title(f"Volume for {ticker.upper()} expiring {expiry}")
|
||||
plt.legend(loc=0)
|
||||
fig.tight_layout(pad=1)
|
||||
|
||||
plt.show()
|
||||
plt.style.use("default")
|
||||
print("")
|
||||
|
||||
|
||||
def plot_volume_open_interest(
|
||||
ticker: str,
|
||||
exp_date: str,
|
||||
calls: pd.DataFrame,
|
||||
puts: pd.DataFrame,
|
||||
ns_parser: argparse.Namespace,
|
||||
):
|
||||
"""Plot volume and open interest
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ticker : str
|
||||
Main ticker to compare income
|
||||
exp_date : str
|
||||
Expiry date of the option
|
||||
calls: pd.DataFrame
|
||||
Option data calls
|
||||
puts: pd.DataFrame
|
||||
Option data puts
|
||||
ns_parser: argparse.Namespace
|
||||
Parsed namespace
|
||||
"""
|
||||
|
||||
current_price = float(yf.Ticker(ticker).info["regularMarketPrice"])
|
||||
|
||||
# Process Calls Data
|
||||
df_calls = calls.pivot_table(
|
||||
index="strike", values=["volume", "openInterest"], aggfunc="sum"
|
||||
).reindex()
|
||||
df_calls["strike"] = df_calls.index
|
||||
df_calls["type"] = "calls"
|
||||
df_calls["openInterest"] = df_calls["openInterest"]
|
||||
df_calls["volume"] = df_calls["volume"]
|
||||
df_calls["oi+v"] = df_calls["openInterest"] + df_calls["volume"]
|
||||
df_calls["spot"] = round(current_price, 2)
|
||||
|
||||
df_puts = puts.pivot_table(
|
||||
index="strike", values=["volume", "openInterest"], aggfunc="sum"
|
||||
).reindex()
|
||||
df_puts["strike"] = df_puts.index
|
||||
df_puts["type"] = "puts"
|
||||
df_puts["openInterest"] = df_puts["openInterest"]
|
||||
df_puts["volume"] = -df_puts["volume"]
|
||||
df_puts["openInterest"] = -df_puts["openInterest"]
|
||||
df_puts["oi+v"] = df_puts["openInterest"] + df_puts["volume"]
|
||||
df_puts["spot"] = round(current_price, 2)
|
||||
|
||||
call_oi = calls.set_index("strike")["openInterest"] / 1000
|
||||
put_oi = puts.set_index("strike")["openInterest"] / 1000
|
||||
|
||||
df_opt = pd.merge(call_oi, put_oi, left_index=True, right_index=True)
|
||||
df_opt = df_opt.rename(
|
||||
columns={"openInterest_x": "OI_call", "openInterest_y": "OI_put"}
|
||||
)
|
||||
|
||||
max_pain = op_helpers.calculate_max_pain(df_opt)
|
||||
|
||||
if ns_parser.min_vol == -1 and ns_parser.min_sp == -1 and ns_parser.max_sp == -1:
|
||||
# If no argument provided, we use the percentile 50 to get 50% of upper volume data
|
||||
volume_percentile_threshold = 50
|
||||
min_vol_calls = np.percentile(df_calls["oi+v"], volume_percentile_threshold)
|
||||
min_vol_puts = np.percentile(df_puts["oi+v"], volume_percentile_threshold)
|
||||
|
||||
df_calls = df_calls[df_calls["oi+v"] > min_vol_calls]
|
||||
df_puts = df_puts[df_puts["oi+v"] < min_vol_puts]
|
||||
|
||||
else:
|
||||
if ns_parser.min_vol > -1:
|
||||
df_calls = df_calls[df_calls["oi+v"] > ns_parser.min_vol]
|
||||
df_puts = df_puts[df_puts["oi+v"] < -ns_parser.min_vol]
|
||||
|
||||
if ns_parser.min_sp > -1:
|
||||
df_calls = df_calls[df_calls["strike"] > ns_parser.min_sp]
|
||||
df_puts = df_puts[df_puts["strike"] > ns_parser.min_sp]
|
||||
|
||||
if ns_parser.max_sp > -1:
|
||||
df_calls = df_calls[df_calls["strike"] < ns_parser.max_sp]
|
||||
df_puts = df_puts[df_puts["strike"] < ns_parser.max_sp]
|
||||
|
||||
if df_calls.empty and df_puts.empty:
|
||||
print(
|
||||
"The filtering applied is too strong, there is no data available for such conditions.\n"
|
||||
)
|
||||
return
|
||||
|
||||
# Initialize the matplotlib figure
|
||||
_, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfp.PLOT_DPI)
|
||||
|
||||
# make x axis symmetric
|
||||
axis_origin = max(abs(max(df_puts["oi+v"])), abs(max(df_calls["oi+v"])))
|
||||
ax.set_xlim(-axis_origin, +axis_origin)
|
||||
|
||||
sns.set_style(style="darkgrid")
|
||||
|
||||
g = sns.barplot(
|
||||
x="oi+v",
|
||||
y="strike",
|
||||
data=df_calls,
|
||||
label="Calls: Open Interest",
|
||||
color="lightgreen",
|
||||
orient="h",
|
||||
)
|
||||
|
||||
g = sns.barplot(
|
||||
x="volume",
|
||||
y="strike",
|
||||
data=df_calls,
|
||||
label="Calls: Volume",
|
||||
color="green",
|
||||
orient="h",
|
||||
)
|
||||
|
||||
g = sns.barplot(
|
||||
x="oi+v",
|
||||
y="strike",
|
||||
data=df_puts,
|
||||
label="Puts: Open Interest",
|
||||
color="pink",
|
||||
orient="h",
|
||||
)
|
||||
|
||||
g = sns.barplot(
|
||||
x="volume",
|
||||
y="strike",
|
||||
data=df_puts,
|
||||
label="Puts: Volume",
|
||||
color="red",
|
||||
orient="h",
|
||||
)
|
||||
|
||||
# draw spot line
|
||||
s = [float(strike.get_text()) for strike in ax.get_yticklabels()]
|
||||
spot_index = bisect_left(s, current_price) # find where the spot is on the graph
|
||||
spot_line = ax.axhline(spot_index, ls="--", color="dodgerblue", alpha=0.3)
|
||||
|
||||
# draw max pain line
|
||||
max_pain_index = bisect_left(s, max_pain)
|
||||
max_pain_line = ax.axhline(max_pain_index, ls="-", color="black", alpha=0.3)
|
||||
max_pain_line.set_linewidth(3)
|
||||
|
||||
# format ticklabels without - for puts
|
||||
g.set_xticks(g.get_xticks())
|
||||
xlabels = [f"{x:,.0f}".replace("-", "") for x in g.get_xticks()]
|
||||
g.set_xticklabels(xlabels)
|
||||
|
||||
plt.title(
|
||||
f"{ticker} volumes for {exp_date} (open interest displayed only during market hours)"
|
||||
)
|
||||
ax.invert_yaxis()
|
||||
|
||||
_ = ax.legend()
|
||||
handles, _ = ax.get_legend_handles_labels()
|
||||
handles.append(spot_line)
|
||||
handles.append(max_pain_line)
|
||||
|
||||
# create legend labels + add to graph
|
||||
labels = [
|
||||
"Calls open interest",
|
||||
"Calls volume ",
|
||||
"Puts open interest",
|
||||
"Puts volume",
|
||||
"Current stock price",
|
||||
f"Max pain = {max_pain}",
|
||||
]
|
||||
|
||||
plt.legend(handles=handles[:], labels=labels)
|
||||
sns.despine(left=True, bottom=True)
|
||||
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
plt.show()
|
||||
plt.style.use("default")
|
||||
print("")
|
||||
13
terminal.py
13
terminal.py
@@ -432,18 +432,7 @@ Contexts:
|
||||
|
||||
def call_op(self, _):
|
||||
"""Process op command"""
|
||||
if self.interval == "1440min":
|
||||
return op_controller.menu(
|
||||
self.ticker,
|
||||
self.stock,
|
||||
)
|
||||
|
||||
df_stock = yf.download(self.ticker, start=self.start, progress=False)
|
||||
|
||||
return op_controller.menu(
|
||||
self.ticker,
|
||||
df_stock,
|
||||
)
|
||||
return op_controller.menu(self.ticker)
|
||||
|
||||
def call_pred(self, _):
|
||||
"""Process pred command"""
|
||||
|
||||
Reference in New Issue
Block a user