Last active
August 8, 2023 04:53
-
-
Save dharmatech/7b8135785ec22a4e68062ccc8feb99f6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ---------------------------------------------------------------------- | |
# Uses the polygon-api-client package: | |
# https://github.com/polygon-io/client-python | |
# Start the script like this: | |
# python -i polygon-io-websocket-trades-show-if-higher-filtered-table-release.py | |
# If you want to capture all the options websocket data for the day, start it before the market opens. | |
# All the options data is put into the `data` array. Only filtered trades are shown on the console. | |
# Use ctrl-c to stop script at the end of the day (or in the middle of the day for whatever reason). | |
# Use `save()` to save the options data to disk. | |
# If you stop the script in the middle of the day and want to continue collecting data, you can run these lines: | |
# ws = WebSocketClient(feed='socket.polygon.io', market='options', subscriptions=["T.*"], api_key=api_key) | |
# ws.run(handle_msg=handle_msg) | |
# You'll need the 'Advanced' options subscription on polygon-io to run this script. Currently $199/month. | |
# https://polygon.io/pricing?product=options | |
# So unless you have a need to dig into the low level data, you're better off going with Unusual Whales which is much cheaper. | |
# ---------------------------------------------------------------------- | |
import os | |
import json | |
from polygon import WebSocketClient | |
from polygon.websocket.models import WebSocketMessage | |
from typing import List | |
from datetime import datetime | |
import re | |
import pandas as pd | |
import jsonpickle | |
from polygon import RESTClient | |
import time | |
import colorama | |
from colorama import Fore, Style | |
import locale | |
locale.setlocale(locale.LC_ALL, '') | |
# ---------------------------------------------------------------------- | |
api_key = os.environ['POLYGON_IO_API_KEY'] | |
data = [] | |
filtered_table = {} | |
# ---------------------------------------------------------------------- | |
def save(): | |
frozen = jsonpickle.encode(data) | |
path = "data-{}.json".format(datetime.now().strftime('%Y-%m-%d-%H-%M-%S')) | |
file = open(path, "w") | |
n = file.write(frozen) | |
file.close() | |
# ---------------------------------------------------------------------- | |
def hyperlink(url, text): | |
return f'\u001b]8;;{url}\u001b\{text}\u001b]8;;\u001b\\' | |
def uw_flow_link(symbol): | |
return hyperlink(f'https://unusualwhales.com/stock/{symbol}/flow-overview', symbol).ljust(67 + len(symbol)) | |
def uw_live_options_flow(contract, symbol, strike, exp): | |
return hyperlink(f'https://unusualwhales.com/live-options-flow?type=C&expiry_dates[]={exp}&min_strike={strike}&max_strike={strike}&ticker_symbol={symbol}', contract).ljust(156 + len(contract)) | |
def uw_live_options_flow(contract): | |
result = re.search(r'O:(.+)(\d\d)(\d\d)(\d\d)([CP])0*(\d+)$', contract) | |
symbol, year, month, day, contract_type, strike = result.groups() | |
exp = f'20{year}-{month}-{day}' | |
strike = int(strike) / 1000 | |
link = hyperlink(f'https://unusualwhales.com/live-options-flow?type={contract_type}&expiry_dates[]={exp}&min_strike={strike}&max_strike={strike}&ticker_symbol={symbol}', contract) | |
return link.ljust(15+len(link)-len(symbol)) | |
# ---------------------------------------------------------------------- | |
client = RESTClient(api_key=api_key) | |
result_conditions = client.list_conditions(asset_class='options', limit=1000) | |
conditions = list(result_conditions) | |
conditions_table = {} | |
for condition in conditions: | |
conditions_table[condition.id] = condition | |
# ---------------------------------------------------------------------- | |
def print_trade (trade): | |
symbol = re.sub(r'^O:', r'', trade.symbol) # remove before symbol | |
symbol = re.sub(r'2.+$', r'', symbol) # remove after symbol | |
result = re.search(r'(\d\d\d\d\d\d)[CP]', trade.symbol) | |
exp = result.group(1) | |
result = re.search(r'\d\d\d\d\d\d[CP]0*(\d+)', trade.symbol) | |
strike = result.group(1) | |
strike = int(strike) / 1000 | |
result = re.search(r'(\d\d\d\d\d\d)([CP])0*(\d+)', trade.symbol) | |
contract_type = result.group(2) | |
premium = int(trade.price * trade.size * 100) | |
condition_names = [conditions_table[id].name for id in trade.conditions] | |
condition_abbreviations = [conditions_table[id].abbreviation for id in trade.conditions] | |
time = datetime.fromtimestamp(trade.timestamp / 1000) | |
bid_price = '' | |
ask_price = '' | |
side = '' | |
flag = '' | |
if hasattr(trade, 'bid_price'): | |
bid_price = f'{trade.bid_price:6.2f}' | |
else: | |
bid_price = ' ' | |
if hasattr(trade, 'ask_price'): | |
ask_price = f'{trade.ask_price:6.2f}' | |
else: | |
ask_price = ' ' | |
if hasattr(trade, 'bid_price'): | |
spread = trade.ask_price - trade.bid_price | |
mid = (trade.ask_price + trade.bid_price) / 2 | |
if trade.price > mid + spread * 0.1: | |
side = 'ask' | |
elif trade.price < mid - spread * 0.1: | |
side = 'bid' | |
else: | |
side = 'mid' | |
bullish = f'{Fore.GREEN}bullish{Style.RESET_ALL}' | |
bearish = f'{Fore.RED}bearish{Style.RESET_ALL}' | |
neutral = f'{Fore.BLUE}neutral{Style.RESET_ALL}' | |
if contract_type == 'C': | |
if side == 'ask': | |
flag = bullish | |
elif side == 'bid': | |
flag = bearish | |
elif side == 'mid': | |
flag = neutral | |
elif contract_type == 'P': | |
if side == 'ask': | |
flag = bearish | |
elif side == 'bid': | |
flag = bullish | |
elif side == 'mid': | |
flag = neutral | |
if contract_type == 'C': | |
contract_type = f'{Fore.GREEN}{contract_type}{Style.RESET_ALL}' | |
elif contract_type == 'P': | |
contract_type = f'{Fore.RED}{contract_type}{Style.RESET_ALL}' | |
if side == 'ask': | |
side = f'{Fore.GREEN}{side}{Style.RESET_ALL}' | |
elif side == 'bid': | |
side = f'{Fore.RED}{side}{Style.RESET_ALL}' | |
else: | |
side = f'{Fore.BLUE}{side}{Style.RESET_ALL}' | |
symbol = uw_flow_link(symbol) | |
contract = uw_live_options_flow(trade.symbol) | |
# contract = trade.symbol | |
print(f'{time:%H:%M:%S.%f}', f'{symbol:5}', f'{exp[0:2]}-{exp[2:4]}-{exp[4:6]}', f'{contract_type:2}', f'{strike:8.2f}', | |
# f'{trade.symbol:25}', | |
f'{contract}' | |
f'{bid_price}', f'{trade.price:6.2f}', f'{ask_price}', f'{side:4}', | |
f'{trade.size:7n}', f'{premium:15n}', flag, f'{condition_abbreviations[0]}', f'{len(condition_abbreviations)}', f'{condition_names[0]}') | |
def print_trade_highlight (trade): | |
premium = int(trade.price * trade.size * 100) | |
print(trade.timestamp, f'{Fore.YELLOW}{trade.symbol:30}{Style.RESET_ALL}', f'{premium:15n}') | |
thresholds = { | |
'SPX' : 2000000, | |
'SPXW' : 2000000, | |
'NVDA' : 500000, | |
'QQQ' : 350000, | |
'META' : 800000, | |
'NFLX' : 300000, | |
'TSLA' : 400000, | |
'PLTR' : 150000, | |
'SPY' : 500000, | |
'NDXP' : 200000, | |
'GOOGL' : 150000, | |
'NDX' : 200000, | |
'RUT' : 150000, | |
'RUTW' : 150000, | |
'IWM' : 500000, | |
'AMD' : 200000, | |
'MSFT' : 200000, | |
'AMZN' : 150000, | |
'BABA' : 150000, | |
'SOFI' : 100000, | |
'ROKU' : 200000, | |
'AI' : 50000, | |
'RTX' : 30000, | |
'FXI' : 30000, | |
'ONON' : 30000, | |
'TRUP' : 40000, | |
'PINS' : 20000, | |
'ATVI' : 20000, | |
'INTU' : 20000, | |
'AAPL' : 2000000 | |
} | |
# ---------------------------------------------------------------------- | |
from threading import Thread | |
def handle_trade(trade): | |
print('handle_trade start') | |
print(trade) | |
time.sleep(3) | |
print('handle_trade complete') | |
def handle_msg_test(msg: List[WebSocketMessage]): | |
for m in msg: | |
thread = Thread(target=handle_trade, args=(m,)) | |
thread.start() | |
# ---------------------------------------------------------------------- | |
def handle_msg(msg: List[WebSocketMessage]): | |
for m in msg: | |
data.append(m) | |
symbol = re.sub(r'^O:', r'', m.symbol) # remove before symbol | |
symbol = re.sub(r'2.+$', r'', symbol) # remove after symbol | |
premium = m.price * m.size * 100 | |
# -------------------------------------------------- | |
if symbol not in filtered_table: | |
filtered_table[symbol] = [] | |
filtered = filtered_table[symbol] | |
# -------------------------------------------------- | |
if premium > thresholds.get(symbol, 100000): | |
ls = [trade for trade in filtered if symbol in trade.symbol] | |
if all(premium > trade.price * trade.size * 100 for trade in ls): | |
# last_quote = client.get_last_quote(ticker=trade.symbol) | |
# trade.bid_price = last_quote.bid_price | |
# trade.ask_price = last_quote.ask_price | |
last_quote = client.get_last_quote(ticker=m.symbol) | |
m.bid_price = last_quote.bid_price | |
m.ask_price = last_quote.ask_price | |
print_trade(m) | |
filtered.append(m) | |
# ---------------------------------------------------------------------- | |
ws = WebSocketClient(feed='socket.polygon.io', market='options', subscriptions=["T.*"], api_key=api_key) | |
ws.run(handle_msg=handle_msg) | |
# ---------------------------------------------------------------------- | |
def save_items(items): | |
frozen = jsonpickle.encode(items) | |
path = "items-{}.json".format(datetime.now().strftime('%Y-%m-%d-%H-%M-%S')) | |
file = open(path, "w") | |
n = file.write(frozen) | |
file.close() | |
# ---------------------------------------------------------------------- | |
# get trades with a bid_price | |
# ---------------------------------------------------------------------- | |
# trades = [trade for trade in data if hasattr(trade, 'bid_price')] | |
# [print_trade(t) for t in trades[:50]] # first 50 elements | |
# [print_trade(t) for t in trades[-30:]] # last 30 elements | |
# ---------------------------------------------------------------------- |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment