add options flow to discord bot
This commit is contained in:
parent
afc9ad884a
commit
1001cf4381
@ -1,26 +1,30 @@
|
|||||||
import requests
|
import requests
|
||||||
import time
|
import time
|
||||||
import orjson
|
import orjson
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone, date
|
||||||
|
import pytz
|
||||||
import os
|
import os
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
|
ny_tz = pytz.timezone("America/New_York")
|
||||||
|
|
||||||
today = datetime.utcnow().date()
|
today = datetime.utcnow().date()
|
||||||
now = datetime.now(timezone.utc)
|
now = datetime.now(timezone.utc)
|
||||||
|
now_ny = datetime.now(ny_tz)
|
||||||
N_minutes_ago = now - timedelta(minutes=30)
|
N_minutes_ago = now - timedelta(minutes=30)
|
||||||
|
|
||||||
WEBHOOK_URL = os.getenv("DISCORD_DARK_POOL_WEBHOOK")
|
DARK_POOL_WEBHOOK_URL = os.getenv("DISCORD_DARK_POOL_WEBHOOK")
|
||||||
|
OPTIONS_FLOW_WEBHOOK_URL = os.getenv("DISCORD_OPTIONS_FLOW_WEBHOOK")
|
||||||
|
|
||||||
|
|
||||||
def save_json(data):
|
def save_json(data):
|
||||||
directory = "json/discord"
|
directory = "json/discord"
|
||||||
try:
|
|
||||||
os.makedirs(directory, exist_ok=True)
|
os.makedirs(directory, exist_ok=True)
|
||||||
with open(directory+"/dark_pool.json", 'wb') as file:
|
with open(directory+"/dark_pool.json", 'wb') as file:
|
||||||
file.write(orjson.dumps(data))
|
file.write(orjson.dumps(data))
|
||||||
except Exception as e:
|
|
||||||
print(f"An error occurred while saving data: {e}")
|
|
||||||
|
|
||||||
|
|
||||||
def format_number(num, decimal=False):
|
def format_number(num, decimal=False):
|
||||||
@ -118,7 +122,7 @@ def dark_pool_flow():
|
|||||||
"embeds": [embed]
|
"embeds": [embed]
|
||||||
}
|
}
|
||||||
|
|
||||||
response = requests.post(WEBHOOK_URL, json=payload)
|
response = requests.post(DARK_POOL_WEBHOOK_URL, json=payload)
|
||||||
|
|
||||||
if response.status_code in (200, 204):
|
if response.status_code in (200, 204):
|
||||||
seen_list.append({'date': result['date'], 'trackingID': result['trackingID']})
|
seen_list.append({'date': result['date'], 'trackingID': result['trackingID']})
|
||||||
@ -133,5 +137,126 @@ def dark_pool_flow():
|
|||||||
else:
|
else:
|
||||||
print("Dark pool already sent!")
|
print("Dark pool already sent!")
|
||||||
|
|
||||||
|
|
||||||
|
def options_flow():
|
||||||
|
now_ny = datetime.now(ny_tz)
|
||||||
|
N_minutes_ago = now_ny - timedelta(minutes=5)
|
||||||
|
today = now_ny.date()
|
||||||
|
|
||||||
|
# Load seen entries
|
||||||
|
try:
|
||||||
|
with open("json/discord/options_flow.json", "rb") as file:
|
||||||
|
seen_list = orjson.loads(file.read())
|
||||||
|
today_iso = today.isoformat()
|
||||||
|
seen_list = [item for item in seen_list if item['date'] == today_iso]
|
||||||
|
except FileNotFoundError:
|
||||||
|
seen_list = []
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error loading seen list: {e}")
|
||||||
|
seen_list = []
|
||||||
|
|
||||||
|
# Load current data
|
||||||
|
try:
|
||||||
|
with open("json/options-flow/feed/data.json", "rb") as file:
|
||||||
|
res_list = orjson.loads(file.read())
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error loading data.json: {e}")
|
||||||
|
res_list = []
|
||||||
|
|
||||||
|
# Process and filter entries
|
||||||
|
filtered = []
|
||||||
|
for item in res_list:
|
||||||
|
try:
|
||||||
|
# Validate required fields
|
||||||
|
if float(item.get('cost_basis', 0)) < 1E5:
|
||||||
|
continue
|
||||||
|
|
||||||
|
item_date = datetime.fromisoformat(item['date']).date()
|
||||||
|
if item_date != today:
|
||||||
|
continue
|
||||||
|
|
||||||
|
item_time = datetime.strptime(item['time'], "%H:%M:%S").time()
|
||||||
|
item_dt = ny_tz.localize(datetime.combine(item_date, item_time))
|
||||||
|
if item_dt < N_minutes_ago:
|
||||||
|
filtered.append(item)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error processing item {item.get('id')}: {e}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not filtered:
|
||||||
|
print("No recent valid entries found")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Find best order
|
||||||
|
best_order = max(filtered, key=lambda x: float(x['cost_basis']))
|
||||||
|
result = {
|
||||||
|
k: best_order[k] for k in [
|
||||||
|
'date', 'sentiment', 'option_activity_type', 'ticker', 'id',
|
||||||
|
'strike_price', 'date_expiration', 'size', 'cost_basis',
|
||||||
|
'execution_estimate', 'volume', 'open_interest', 'put_call'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check duplicates
|
||||||
|
seen_ids = {item['id'] for item in seen_list}
|
||||||
|
if result['id'] in seen_ids:
|
||||||
|
print("Options Flow data already sent!")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Prepare message
|
||||||
|
try:
|
||||||
|
symbol = result['ticker']
|
||||||
|
size = format_number(result['size'])
|
||||||
|
premium = format_number(result['cost_basis'])
|
||||||
|
strike = result['strike_price']
|
||||||
|
side = result['execution_estimate']
|
||||||
|
volume = format_number(result['volume'])
|
||||||
|
open_interest = format_number(result['open_interest'])
|
||||||
|
put_call = result['put_call'].replace('Calls', 'Call').replace('Puts', 'Put')
|
||||||
|
option_activity_type = result['option_activity_type']
|
||||||
|
sentiment = result['sentiment']
|
||||||
|
|
||||||
|
date_expiration = datetime.strptime(result['date_expiration'], "%Y-%m-%d").strftime("%d/%m/%Y")
|
||||||
|
message_timestamp = int(now_ny.timestamp())
|
||||||
|
|
||||||
|
color = 0x39FF14 if sentiment == 'Bullish' else 0xFF0000 if sentiment == 'Bearish' else 0xFFA500
|
||||||
|
|
||||||
|
embed = {
|
||||||
|
"color": color,
|
||||||
|
"thumbnail": {"url": "https://stocknear.com/pwa-64x64.png"},
|
||||||
|
"description": f"{put_call} {option_activity_type} ({sentiment})",
|
||||||
|
"fields": [
|
||||||
|
{"name": "Symbol", "value": symbol, "inline": True},
|
||||||
|
{"name": "Strike", "value": str(strike), "inline": True},
|
||||||
|
{"name": "Expiration", "value": date_expiration, "inline": True},
|
||||||
|
{"name": "Call/Put", "value": put_call, "inline": True},
|
||||||
|
{"name": "Side", "value": str(side), "inline": True},
|
||||||
|
{"name": "Size", "value": str(size), "inline": True},
|
||||||
|
{"name": "Premium", "value": f"${premium}", "inline": True},
|
||||||
|
{"name": "Volume", "value": str(volume), "inline": True},
|
||||||
|
{"name": "OI", "value": str(open_interest), "inline": True},
|
||||||
|
{"name": f"Data by Stocknear - <t:{message_timestamp}:R> - Delayed by 5 min",
|
||||||
|
"value": "", "inline": False}
|
||||||
|
],
|
||||||
|
"footer": {"text": ""}
|
||||||
|
}
|
||||||
|
|
||||||
|
payload = {"content": "", "embeds": [embed]}
|
||||||
|
|
||||||
|
# Send to Discord
|
||||||
|
response = requests.post(OPTIONS_FLOW_WEBHOOK_URL, json=payload)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
# Update seen list
|
||||||
|
seen_list.append({'date': result['date'], 'id': result['id']})
|
||||||
|
with open("json/discord/options_flow.json", "wb") as file:
|
||||||
|
file.write(orjson.dumps(seen_list))
|
||||||
|
print("Embed sent successfully!")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error sending message: {e}")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
options_flow()
|
||||||
dark_pool_flow()
|
dark_pool_flow()
|
||||||
Loading…
x
Reference in New Issue
Block a user