add recent earnings to discord bot

This commit is contained in:
MuslemRahimi 2025-04-11 17:12:37 +02:00
parent 1001cf4381
commit 9d093e45b8
2 changed files with 244 additions and 7 deletions

View File

@ -1,11 +1,15 @@
import requests
import time
import orjson
import aiohttp
import aiofiles
from datetime import datetime, timedelta, timezone, date
import pytz
import os
from dotenv import load_dotenv
import asyncio
import sqlite3
import hashlib
load_dotenv()
@ -18,7 +22,10 @@ N_minutes_ago = now - timedelta(minutes=30)
DARK_POOL_WEBHOOK_URL = os.getenv("DISCORD_DARK_POOL_WEBHOOK")
OPTIONS_FLOW_WEBHOOK_URL = os.getenv("DISCORD_OPTIONS_FLOW_WEBHOOK")
RECENT_EARNINGS_WEBHOOK_URL = os.getenv("DISCORD_RECENT_EARNINGS_WEBHOOK")
BENZINGA_API_KEY = os.getenv('BENZINGA_API_KEY')
headers = {"accept": "application/json"}
def save_json(data):
directory = "json/discord"
@ -26,7 +33,6 @@ def save_json(data):
with open(directory+"/dark_pool.json", 'wb') as file:
file.write(orjson.dumps(data))
def format_number(num, decimal=False):
"""Abbreviate large numbers with B/M suffix and format appropriately"""
# Handle scientific notation formats like 5E6
@ -59,6 +65,130 @@ def format_number(num, decimal=False):
else:
return f"{num:,.0f}" # Format smaller numbers with commas
def abbreviate_number(n):
"""
Abbreviate a number to a readable format.
E.g., 1500 -> '1.5K', 2300000 -> '2.3M'
"""
if n is None:
return "N/A"
abs_n = abs(n)
if abs_n < 1000:
return str(n)
elif abs_n < 1_000_000:
return f"{n/1000:.1f}K"
elif abs_n < 1_000_000_000:
return f"{n/1_000_000:.1f}M"
else:
return f"{n/1_000_000_000:.1f}B"
def remove_duplicates(elements):
seen = set()
unique_elements = []
for element in elements:
if element['symbol'] not in seen:
seen.add(element['symbol'])
unique_elements.append(element)
return unique_elements
def weekday():
today = datetime.today()
if today.weekday() >= 5: # 5 = Saturday, 6 = Sunday
yesterday = today - timedelta(2)
else:
yesterday = today - timedelta(1)
return yesterday.strftime('%Y-%m-%d')
async def get_recent_earnings(session):
today = datetime.today().strftime('%Y-%m-%d')
yesterday = weekday()
url = "https://api.benzinga.com/api/v2.1/calendar/earnings"
res_list = []
importance_list = ["1","2","3","4","5"]
for importance in importance_list:
querystring = {
"token": BENZINGA_API_KEY,
"parameters[importance]": importance,
"parameters[date_from]": yesterday,
"parameters[date_to]": today,
"parameters[date_sort]": "date"
}
try:
async with session.get(url, params=querystring, headers=headers) as response:
res = orjson.loads(await response.text())['earnings']
for item in res:
try:
symbol = item['ticker']
name = item['name']
time = item['time']
date = item['date']
updated = int(item['updated']) # Convert to integer for proper comparison
# Convert numeric fields, handling empty strings
eps_prior = float(item['eps_prior']) if item['eps_prior'] != '' else None
eps_surprise = float(item['eps_surprise']) if item['eps_surprise'] != '' else None
eps = float(item['eps']) if item['eps'] != '' else 0
revenue_prior = float(item['revenue_prior']) if item['revenue_prior'] != '' else None
revenue_surprise = float(item['revenue_surprise']) if item['revenue_surprise'] != '' else None
revenue = float(item['revenue']) if item['revenue'] != '' else None
if (symbol in stock_symbols and
revenue is not None and
revenue_prior is not None and
eps_prior is not None and
eps is not None and
revenue_surprise is not None and
eps_surprise is not None):
with open(f"json/quote/{symbol}.json","r") as file:
quote_data = orjson.loads(file.read())
market_cap = quote_data.get('marketCap',0)
price = quote_data.get('price',0)
changes_percentage = quote_data.get('changesPercentage',0)
res_list.append({
'symbol': symbol,
'name': name,
'time': time,
'date': date,
'marketCap': market_cap,
'epsPrior': eps_prior,
'epsSurprise': eps_surprise,
'eps': eps,
'revenuePrior': revenue_prior,
'revenueSurprise': revenue_surprise,
'revenue': revenue,
'price': price,
'changesPercentage': changes_percentage,
'updated': updated
})
except Exception as e:
print('Recent Earnings:', e)
pass
except Exception as e:
print('API Request Error:', e)
pass
# Remove duplicates
res_list = remove_duplicates(res_list)
# Sort first by the most recent 'updated' timestamp, then by market cap
res_list.sort(key=lambda x: (-x['updated'], -x['marketCap']))
# Remove market cap before returning and limit to top 10
res_list = [{k: v for k, v in d.items() if k not in ['updated']} for d in res_list]
return res_list[:10]
def dark_pool_flow():
try:
@ -70,7 +200,7 @@ def dark_pool_flow():
with open(f"json/dark-pool/feed/data.json", "r") as file:
res_list = orjson.loads(file.read())
res_list = [item for item in res_list if float(item['premium']) >= 30E6 and datetime.fromisoformat(item['date']).date() == today]
res_list = [item for item in res_list if float(item['premium']) >= 100E6 and datetime.fromisoformat(item['date']).date() == today]
if res_list:
@ -101,7 +231,7 @@ def dark_pool_flow():
message_timestamp = int((datetime.now() - timedelta(minutes=0)).timestamp())
embed = {
"color": 0xC475FD, # Green color from original embed
"color": 0xC475FD,
"thumbnail": {"url": "https://stocknear.com/pwa-64x64.png"},
"title": "Dark Pool Order",
"fields": [
@ -256,7 +386,114 @@ def options_flow():
except Exception as e:
print(f"Error sending message: {e}")
async def recent_earnings_message():
if __name__ == "__main__":
try:
with open(f"json/discord/recent_earnings.json", "r") as file:
seen_list = orjson.loads(file.read())
seen_list = [item for item in seen_list if datetime.fromisoformat(item['date']).date() == today]
except:
seen_list = []
async with aiohttp.ClientSession() as session:
res_list = await get_recent_earnings(session)
for item in res_list:
unique_str = f"{item['date']}-{item['symbol']}-{item['revenue']}-{item['eps']}"
item['id'] = hashlib.md5(unique_str.encode()).hexdigest()
if res_list:
if seen_list:
seen_ids = {item['id'] for item in seen_list}
else:
seen_ids = {}
for item in res_list:
try:
if item != None and item['id'] not in seen_ids and item['marketCap'] > 100E9:
symbol = item['symbol']
price = item['price']
changes_percentage = round(item['changesPercentage'],2)
revenue = abbreviate_number(item['revenue'])
revenue_surprise = abbreviate_number(item.get("revenueSurprise", 0))
eps = item['eps']
eps_surprise = item.get("epsSurprise", 0)
revenue_surprise_text = "exceeds" if item["revenueSurprise"] > 0 else "misses"
eps_surprise_text = "exceeds" if eps_surprise > 0 else "misses"
market_cap = abbreviate_number(item['marketCap'])
revenue_yoy_change = (item["revenue"] / item["revenuePrior"] - 1) * 100
revenue_yoy_direction = "decline" if (item["revenue"] / item["revenuePrior"] - 1) < 0 else "growth"
eps_yoy_change = (item["eps"] / item["epsPrior"] - 1) * 100
eps_yoy_direction = "decline" if (item["eps"] / item["epsPrior"] - 1) < 0 else "growth"
message_timestamp = int((datetime.now() - timedelta(minutes=0)).timestamp())
embed = {
"color": 0xC475FD,
"thumbnail": {"url": "https://stocknear.com/pwa-64x64.png"},
"title": "Earnings Surprise",
"fields": [
{"name": "Symbol", "value": symbol, "inline": True},
{"name": "", "value": "", "inline": True},
{"name": "Market Cap", "value": market_cap, "inline": True},
{"name": "Price", "value": str(price), "inline": True},
{"name": "", "value": "", "inline": True},
{"name": "% Change", "value": str(changes_percentage)+"%", "inline": True},
{"name": "", "value": "", "inline": False},
{"name": f"Revenue of {revenue} {revenue_surprise_text} estimates by {revenue_surprise}, with {revenue_yoy_change:.2f}% YoY {revenue_yoy_direction}.", "value": "", "inline": False},
{"name": f"EPS of {eps} {eps_surprise_text} estimates by {eps_surprise}, with {eps_yoy_change:.2f}% YoY {eps_yoy_direction}.", "value": "", "inline": False},
{"name": f"Data by Stocknear - <t:{message_timestamp}:R>", "value": "", "inline": False},
],
"footer": {"text": ""}
}
payload = {
"content": "",
"embeds": [embed]
}
response = requests.post(RECENT_EARNINGS_WEBHOOK_URL, json=payload)
if response.status_code in (200, 204):
seen_list.append({'date': item['date'], 'id': item['id'], 'symbol': symbol})
print("Embed sent successfully!")
else:
print(f"Failed to send embed. Status code: {response.status_code}")
print("Response content:", response.text)
else:
print("Earnings already sent!")
except Exception as e:
print(e)
try:
with open("json/discord/recent_earnings.json","wb") as file:
file.write(orjson.dumps(seen_list))
except:
pass
async def main():
options_flow()
dark_pool_flow()
dark_pool_flow()
await recent_earnings_message()
try:
con = sqlite3.connect('stocks.db')
cursor = con.cursor()
cursor.execute("PRAGMA journal_mode = wal")
cursor.execute("SELECT DISTINCT symbol FROM stocks")
stock_symbols = [row[0] for row in cursor.fetchall()]
con.close()
asyncio.run(main())
except Exception as e:
print(e)

View File

@ -427,7 +427,7 @@ schedule.every(5).minutes.do(run_threaded, run_push_notifications).tag('push_not
schedule.every(5).minutes.do(run_threaded, run_market_flow).tag('market_flow_job')
schedule.every(5).minutes.do(run_threaded, run_list).tag('stock_list_job')
schedule.every(5).minutes.do(run_threaded, run_discord_bot).tag('discord_bot')
schedule.every(2).minutes.do(run_threaded, run_discord_bot).tag('discord_bot')