Initial commit: Discord stock bot with hourly PYPL updates

Functional Discord bot with automated hourly stock price updates during NYSE trading hours. Supports manual queries for any ticker via prefix commands.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Michael Simard
2025-12-02 22:25:37 -06:00
commit 3b6f0cbe4a
15 changed files with 1321 additions and 0 deletions

121
market_hours.py Normal file
View File

@@ -0,0 +1,121 @@
from datetime import datetime, time
import pytz
import logging
logger = logging.getLogger(__name__)
class MarketHours:
"""
Utility class for determining NYSE market hours.
Market is open 9:30 AM - 4:00 PM ET, Monday-Friday (excluding holidays).
"""
NYSE_TIMEZONE = pytz.timezone('America/New_York')
MARKET_OPEN = time(9, 30)
MARKET_CLOSE = time(16, 0)
# Major NYSE holidays (this is a simplified list - production systems would use a holiday calendar API)
HOLIDAYS_2024 = [
datetime(2024, 1, 1), # New Year's Day
datetime(2024, 1, 15), # MLK Day
datetime(2024, 2, 19), # Presidents Day
datetime(2024, 3, 29), # Good Friday
datetime(2024, 5, 27), # Memorial Day
datetime(2024, 6, 19), # Juneteenth
datetime(2024, 7, 4), # Independence Day
datetime(2024, 9, 2), # Labor Day
datetime(2024, 11, 28), # Thanksgiving
datetime(2024, 12, 25), # Christmas
]
HOLIDAYS_2025 = [
datetime(2025, 1, 1), # New Year's Day
datetime(2025, 1, 20), # MLK Day
datetime(2025, 2, 17), # Presidents Day
datetime(2025, 4, 18), # Good Friday
datetime(2025, 5, 26), # Memorial Day
datetime(2025, 6, 19), # Juneteenth
datetime(2025, 7, 4), # Independence Day
datetime(2025, 9, 1), # Labor Day
datetime(2025, 11, 27), # Thanksgiving
datetime(2025, 12, 25), # Christmas
]
@classmethod
def is_market_open(cls, check_time: datetime = None) -> bool:
"""
Determine if the NYSE is currently open.
Args:
check_time: Datetime to check (defaults to now)
Returns:
True if market is open, False otherwise
"""
if check_time is None:
check_time = datetime.now(cls.NYSE_TIMEZONE)
else:
check_time = check_time.astimezone(cls.NYSE_TIMEZONE)
# Check if weekend
if check_time.weekday() >= 5: # Saturday = 5, Sunday = 6
logger.debug("Market closed: Weekend")
return False
# Check if holiday
check_date = check_time.date()
all_holidays = cls.HOLIDAYS_2024 + cls.HOLIDAYS_2025
if any(holiday.date() == check_date for holiday in all_holidays):
logger.debug(f"Market closed: Holiday ({check_date})")
return False
# Check if within market hours
current_time = check_time.time()
if cls.MARKET_OPEN <= current_time < cls.MARKET_CLOSE:
return True
else:
logger.debug(f"Market closed: Outside trading hours ({current_time})")
return False
@classmethod
def get_next_market_open(cls, from_time: datetime = None) -> datetime:
"""
Calculate the next time the market will open.
Args:
from_time: Starting datetime (defaults to now)
Returns:
Datetime of next market open
"""
if from_time is None:
from_time = datetime.now(cls.NYSE_TIMEZONE)
else:
from_time = from_time.astimezone(cls.NYSE_TIMEZONE)
# Start checking from the next day at market open
next_day = from_time.replace(hour=cls.MARKET_OPEN.hour,
minute=cls.MARKET_OPEN.minute,
second=0,
microsecond=0)
# If we have not passed today's open time, check today first
if from_time.time() < cls.MARKET_OPEN:
next_day = next_day
else:
# Otherwise start from tomorrow
next_day = next_day.replace(day=next_day.day + 1)
# Find the next valid market day
max_iterations = 14 # Search up to 2 weeks ahead
for _ in range(max_iterations):
if cls.is_market_open(next_day):
return next_day
# Move to next day
next_day = next_day.replace(day=next_day.day + 1)
# Fallback (should not reach here under normal circumstances)
logger.warning("Could not determine next market open within 2 weeks")
return next_day