Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,12 @@ You can also check the `/examples/` directory if you're really desparate, but it

`option_chain = options_data.get_chain_day('FUN')`

- Both `get_chain_day` and `get_chain_all` accept an optional `greeks` flag (default `False`). Set `greeks=True` if you need Tradier's delta, gamma, theta, etc. included in the returned dataframe.

- Get a Stock's Option Chain with Greeks:

`option_chain = options_data.get_chain_day('FUN', greeks=True)`

- Get Nearest Expiry Date to a Specified Number of Days into Future:

`option_closest_expiry = options_data.get_closest_expiry('FUN', num_days=45)`
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

setup(
name='uvatradier',
version='0.4.7',
version='0.4.8',
author='tom hammons',
description='wahoowa',
long_description=long_description,
Expand Down
56 changes: 43 additions & 13 deletions uvatradier/options_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import pandas as pd
import re
from datetime import datetime, timedelta;
from collections.abc import Hashable


class OptionsData (Tradier):
Expand All @@ -23,13 +24,13 @@ def __init__ (self, account_number, auth_token, live_trade=False):
# Fetch all option chain data across all available expiries
#

def get_chain_all (self, symbol):
def get_chain_all (self, symbol, greeks=False):
expiry_dates = self.get_expiry_dates(symbol)

df = pd.DataFrame()

for expiry in expiry_dates:
df = pd.concat([df, self.get_chain_day(symbol=symbol, expiry=expiry)])
df = pd.concat([df, self.get_chain_day(symbol=symbol, expiry=expiry, greeks=greeks)])

return df

Expand All @@ -39,7 +40,7 @@ def get_chain_all (self, symbol):
# Fetch all option chain data for a single day of contract expirations
#

def get_chain_day (self, symbol, expiry='', strike=False, strike_low=False, strike_high=False, option_type=False):
def get_chain_day (self, symbol, expiry='', strike=False, strike_low=False, strike_high=False, option_type=False, greeks=False):
'''
This function returns option chain data for a given symbol.
All contract expirations occur on the same expiry date
Expand All @@ -56,32 +57,61 @@ def get_chain_day (self, symbol, expiry='', strike=False, strike_low=False, stri
# Define request object for given symbol and expiration
#

r = requests.get(
url = f"{self.BASE_URL}/{self.OPTIONS_CHAIN_ENDPOINT}",
params = {'symbol':symbol, 'expiration':expiry, 'greeks':'false'},
headers = self.REQUESTS_HEADERS
);
def _bool_to_flag(value):
if isinstance(value, str):
return 'true' if value.strip().lower() in ['true', '1', 'yes'] else 'false';
return 'true' if bool(value) else 'false';

request_params = {
'symbol' : symbol,
'expiration': expiry,
'greeks' : _bool_to_flag(greeks) if greeks is not None else 'false'
};

try:
r = requests.get(
url = f"{self.BASE_URL}/{self.OPTIONS_CHAIN_ENDPOINT}",
params = request_params,
headers = self.REQUESTS_HEADERS
);
r.raise_for_status();
except requests.exceptions.RequestException as exc:
raise RuntimeError(f"Failed to retrieve option chain for {symbol} ({expiry}): {exc}");

#
# Convert returned json -> pandas dataframe
#

option_df = pd.DataFrame(r.json()['options']['option']);
response_json = r.json() or {};
options_payload = response_json.get('options');
option_rows = options_payload.get('option') if isinstance(options_payload, dict) else None;

if not option_rows:
raise ValueError(f"No option chain data returned for symbol '{symbol}' and expiration '{expiry}'.");

option_df = pd.DataFrame(option_rows);


#
# Remove columns which have the same value for every row
#

cols_to_drop = option_df.nunique()[option_df.nunique() == 1].index;
option_df = option_df.drop(cols_to_drop, axis=1);
hashable_cols = [
col for col in option_df.columns
if option_df[col].map(lambda val: isinstance(val, Hashable) or pd.isna(val)).all()
];

if hashable_cols:
nunique_counts = option_df[hashable_cols].nunique();
cols_to_drop = nunique_counts[nunique_counts == 1].index;
option_df = option_df.drop(cols_to_drop, axis=1);

#
# Remove columns which have NaN in every row
#

cols_to_drop = option_df.nunique()[option_df.nunique() == 0].index;
option_df = option_df.drop(cols_to_drop, axis=1);
empty_cols = option_df.columns[~option_df.notna().any()];
option_df = option_df.drop(empty_cols, axis=1);


#
Expand Down
52 changes: 41 additions & 11 deletions uvatradier/tradier.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import requests;
import numpy as np;
import pandas as pd;
from collections.abc import Hashable

import datetime;
from datetime import datetime, timedelta; # for fetching option expiries
Expand Down Expand Up @@ -547,7 +548,7 @@ def __init__ (self, account_number, auth_token):
# Fetch all option chain data for a single day of contract expirations
#

def get_chain_day (self, symbol, expiry='', strike=False, strike_low=False, strike_high=False, option_type=False):
def get_chain_day (self, symbol, expiry='', strike=False, strike_low=False, strike_high=False, option_type=False, greeks=False):
'''
This function returns option chain data for a given symbol.
All contract expirations occur on the same expiry date
Expand All @@ -564,32 +565,61 @@ def get_chain_day (self, symbol, expiry='', strike=False, strike_low=False, stri
# Define request object for given symbol and expiration
#

r = requests.get(
url = '{}/{}'.format(self.SANDBOX_URL, self.OPTIONS_CHAIN_ENDPOINT),
params = {'symbol':symbol, 'expiration':expiry, 'greeks':'false'},
headers = self.REQUESTS_HEADERS
);
def _bool_to_flag(value):
if isinstance(value, str):
return 'true' if value.strip().lower() in ['true', '1', 'yes'] else 'false';
return 'true' if bool(value) else 'false';

request_params = {
'symbol' : symbol,
'expiration': expiry,
'greeks' : _bool_to_flag(greeks) if greeks is not None else 'false'
};

try:
r = requests.get(
url = '{}/{}'.format(self.SANDBOX_URL, self.OPTIONS_CHAIN_ENDPOINT),
params = request_params,
headers = self.REQUESTS_HEADERS
);
r.raise_for_status();
except requests.exceptions.RequestException as exc:
raise RuntimeError(f"Failed to retrieve option chain for {symbol} ({expiry}): {exc}");

#
# Convert returned json -> pandas dataframe
#

option_df = pd.DataFrame(r.json()['options']['option']);
response_json = r.json() or {};
options_payload = response_json.get('options');
option_rows = options_payload.get('option') if isinstance(options_payload, dict) else None;

if not option_rows:
raise ValueError(f"No option chain data returned for symbol '{symbol}' and expiration '{expiry}'.");

option_df = pd.DataFrame(option_rows);


#
# Remove columns which have the same value for every row
#

cols_to_drop = option_df.nunique()[option_df.nunique() == 1].index;
option_df = option_df.drop(cols_to_drop, axis=1);
hashable_cols = [
col for col in option_df.columns
if option_df[col].map(lambda val: isinstance(val, Hashable) or pd.isna(val)).all()
];

if hashable_cols:
nunique_counts = option_df[hashable_cols].nunique();
cols_to_drop = nunique_counts[nunique_counts == 1].index;
option_df = option_df.drop(cols_to_drop, axis=1);

#
# Remove columns which have NaN in every row
#

cols_to_drop = option_df.nunique()[option_df.nunique() == 0].index;
option_df = option_df.drop(cols_to_drop, axis=1);
empty_cols = option_df.columns[~option_df.notna().any()];
option_df = option_df.drop(empty_cols, axis=1);


#
Expand Down