diff --git a/app/cron_options_gex_dex.py b/app/cron_options_gex_dex.py new file mode 100644 index 0000000..663a375 --- /dev/null +++ b/app/cron_options_gex_dex.py @@ -0,0 +1,117 @@ +import requests +import orjson +import re +from datetime import datetime +from dotenv import load_dotenv +import os +import sqlite3 +import time +from tqdm import tqdm +from concurrent.futures import ThreadPoolExecutor +from functools import partial +import asyncio +import aiohttp + +load_dotenv() + +api_key = os.getenv('UNUSUAL_WHALES_API_KEY') +headers = {"Accept": "application/json, text/plain", "Authorization": api_key} + +# Connect to the databases +con = sqlite3.connect('stocks.db') +etf_con = sqlite3.connect('etf.db') +cursor = con.cursor() +cursor.execute("PRAGMA journal_mode = wal") +#cursor.execute("SELECT DISTINCT symbol FROM stocks WHERE symbol NOT LIKE '%.%' AND marketCap > 1E9") +cursor.execute("SELECT DISTINCT symbol FROM stocks WHERE symbol NOT LIKE '%.%'") +stocks_symbols = [row[0] for row in cursor.fetchall()] + +etf_cursor = etf_con.cursor() +etf_cursor.execute("PRAGMA journal_mode = wal") +#etf_cursor.execute("SELECT DISTINCT symbol FROM etfs WHERE marketCap > 1E9") +etf_cursor.execute("SELECT DISTINCT symbol FROM etfs") +etf_symbols = [row[0] for row in etf_cursor.fetchall()] + +con.close() +etf_con.close() + + +def get_tickers_from_directory(directory: str): + try: + # Ensure the directory exists + if not os.path.exists(directory): + raise FileNotFoundError(f"The directory '{directory}' does not exist.") + + # Get all tickers from filenames + return [file.replace(".json", "") for file in os.listdir(directory) if file.endswith(".json")] + + except Exception as e: + print(f"An error occurred: {e}") + return [] + +directory_path = "json/gex-dex" +total_symbols = get_tickers_from_directory(directory_path) + +if len(total_symbols) < 100: + total_symbols = stocks_symbols+etf_symbols + + +def save_json(data, symbol): + os.makedirs(directory_path, exist_ok=True) # Ensure the directory exists + with open(f"{directory_path}/{symbol}.json", 'wb') as file: # Use binary mode for orjson + file.write(orjson.dumps(data)) + + +def safe_round(value, decimals=2): + try: + return round(float(value), decimals) + except (ValueError, TypeError): + return value + + +def prepare_data(data, symbol): + data = [{k: v for k, v in item.items() if "charm" not in k and "vanna" not in k} for item in data] + res_list = [] + for item in data: + try: + new_item = { + key: safe_round(value) if isinstance(value, (int, float, str)) else value + for key, value in item.items() + } + + res_list.append(new_item) + except: + pass + + if res_list: + res_list = sorted(res_list, key=lambda x: x['date'], reverse=True) + save_json(res_list, symbol) + + +def get_data(): + counter = 0 + total_symbols = ['GME'] + for symbol in tqdm(total_symbols): + try: + url = f"https://api.unusualwhales.com/api/stock/{symbol}/greek-exposure" + + response = requests.get(url, headers=headers) + if response.status_code == 200: + data = response.json()['data'] + prepare_data(data, symbol) + + counter +=1 + + # If 50 chunks have been processed, sleep for 60 seconds + if counter == 260: + print("Sleeping...") + time.sleep(60) + counter = 0 + + except Exception as e: + print(f"Error for {symbol}:{e}") + + + +if __name__ == '__main__': + get_data() \ No newline at end of file diff --git a/app/cron_quote.py b/app/cron_quote.py index 59419e9..d3c8dfa 100755 --- a/app/cron_quote.py +++ b/app/cron_quote.py @@ -27,7 +27,7 @@ def delete_files_in_directory(directory): async def get_quote_of_stocks(ticker_list): ticker_str = ','.join(ticker_list) async with aiohttp.ClientSession() as session: - url = f"https://financialmodelingprep.com/api/v3/quote/{ticker_str}?apikey={api_key}" + url = f"https://financialmodelingprep.com/stable/batch-quote?symbols={ticker_str}&apikey={api_key}" async with session.get(url) as response: if response.status == 200: return await response.json() diff --git a/app/main.py b/app/main.py index efa2ca0..1ef9ac7 100755 --- a/app/main.py +++ b/app/main.py @@ -2658,6 +2658,31 @@ async def get_data(data:GeneralData, api_key: str = Security(get_api_key)): headers={"Content-Encoding": "gzip"} ) +@app.post("/options-gex-dex") +async def get_data(data:TickerData, api_key: str = Security(get_api_key)): + ticker = data.ticker.upper() + cache_key = f"options-gex-dex-{ticker}" + cached_result = redis_client.get(cache_key) + if cached_result: + return StreamingResponse( + io.BytesIO(cached_result), + media_type="application/json", + headers={"Content-Encoding": "gzip"}) + + try: + with open(f"json/gex-dex/{ticker}.json", 'rb') as file: + res = orjson.loads(file.read()) + except: + res = [] + data = orjson.dumps(res) + compressed_data = gzip.compress(data) + redis_client.set(cache_key, compressed_data) + redis_client.expire(cache_key, 3600*60) + return StreamingResponse( + io.BytesIO(compressed_data), + media_type="application/json", + headers={"Content-Encoding": "gzip"} + ) @app.post("/options-stats-ticker") async def get_options_stats_ticker(data:TickerData, api_key: str = Security(get_api_key)):