Add matplotlib-based chart generation with Yahoo Finance data

- Add chart_generator.py with price and candlestick chart support
- Implement Yahoo Finance candle data fetching for free historical data
- Update bot to generate and attach charts to stock embeds
- Add matplotlib dependency to requirements.txt
- Configure dual API approach: Finnhub for quotes, Yahoo for charts

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Michael Simard
2025-12-25 00:25:00 -06:00
parent 3040ab3cc1
commit 71e70b77b0
5 changed files with 290 additions and 8 deletions

49
bot.py
View File

@@ -10,6 +10,7 @@ from config import Config
from stock_api import YahooFinanceAPI, FinnhubAPI
from market_hours import MarketHours
from crypto_api import CoinGeckoAPI
from chart_generator import ChartGenerator
# Configure logging
@@ -36,6 +37,10 @@ class StockBot(commands.Bot):
self.stock_api = YahooFinanceAPI()
logger.info("Using Yahoo Finance API for stock data")
# Always initialize Yahoo Finance for chart data (free historical data)
self.yahoo_api = YahooFinanceAPI()
logger.info("Using Yahoo Finance API for chart data")
self.scheduler = AsyncIOScheduler(timezone=pytz.timezone('America/New_York'))
self.target_channel_ids = [int(id) for id in Config.CHANNEL_IDS]
self.primary_ticker = Config.PRIMARY_TICKER
@@ -254,7 +259,25 @@ class StockBot(commands.Bot):
return
embed = self.create_stock_embed(stock_data)
await channel.send(embed=embed)
# Generate chart using Yahoo Finance historical data
chart_file = None
candle_data = self.yahoo_api.get_candles(ticker, days=30)
if candle_data:
chart_buffer = ChartGenerator.create_price_chart(
ticker,
candle_data,
stock_data.get('company_name')
)
if chart_buffer:
chart_file = discord.File(chart_buffer, filename="chart.png")
# Send with chart attachment if available
if chart_file:
await channel.send(embed=embed, file=chart_file)
else:
await channel.send(embed=embed)
logger.info(f"Sent stock update for {ticker} to channel {channel_id}")
def create_stock_embed(self, stock_data: dict) -> discord.Embed:
@@ -310,9 +333,8 @@ class StockBot(commands.Bot):
embed.add_field(name="Change", value=change_str, inline=True)
embed.add_field(name="Previous Close", value=f"${stock_data['previous_close']}", inline=True)
# Add FinViz daily chart
chart_url = f"https://finviz.com/chart.ashx?t={ticker}&ty=c&ta=1&p=d&s=l"
embed.set_image(url=chart_url)
# Reference attached chart image
embed.set_image(url="attachment://chart.png")
market_status = "🟢 Market Open" if stock_data['market_open'] else "🔴 Market Closed"
embed.set_footer(text=f"{market_status}")
@@ -526,7 +548,24 @@ async def get_stock_price(ctx, ticker: str = None):
return
embed = bot.create_stock_embed(stock_data)
await ctx.send(embed=embed)
# Generate chart using Yahoo Finance historical data
chart_file = None
candle_data = bot.yahoo_api.get_candles(ticker, days=30)
if candle_data:
chart_buffer = ChartGenerator.create_price_chart(
ticker,
candle_data,
stock_data.get('company_name')
)
if chart_buffer:
chart_file = discord.File(chart_buffer, filename="chart.png")
# Send with chart attachment if available
if chart_file:
await ctx.send(embed=embed, file=chart_file)
else:
await ctx.send(embed=embed)
@bot.command(name='crypto', aliases=['c'])