How I Built an AI Stock Research Bot That Saved 18 Hours Per Week
<p>I run a Discord community focused on stock research. Every week, members expect ticker deep dives, sector scans, earnings calendar summaries, and analyst rating breakdowns. Before I built the bot, this cost me 18-20 hours per week of manual research. After deploying the automated system, that dropped to 2-3 hours of review and editing. This is the full story of how I built it, what the architecture looks like, and what I learned along the way.</p>
<h2 id="the-manual-process-death-by-a-thousand-tabs">The manual process: death by a thousand tabs</h2>
<p>Here is what a single ticker deep dive looked like before automation.</p>
<p>First, data gathering (45-60 minutes). Open Finnhub, Yahoo Finance, SEC EDGAR, and 3-4 analyst sites. Pull the company profile, recent earnings, analyst consensus, insider transactions, and sector comparisons. Copy relevant data points into a working document.</p>
<p>Then the analysis (30-45 minutes). Read through the data. Calculate key ratios. Compare against sector averages. Identify the bull case and bear case. Note any unusual patterns: insider buying spikes, earnings revision trends, unusual options activity.</p>
<p>Then report writing (30-45 minutes). Write up the findings in a format the Discord community can actually use. Not a wall of numbers, but a narrative that explains what the data means, what the risks are, and what to watch for. Include charts or tables where they help.</p>
<p>Finally, formatting and delivery (15-20 minutes). Format the report for Discord (character limits, embed formatting, file attachments). Generate a PDF version for members who prefer downloadable reports. Post to the correct channels.</p>
<p>Total per ticker: 2-3 hours. At 8-12 tickers per week, that is 16-36 hours of research. And I was consistently delivering 8 tickers because I physically could not do more. Members wanted more coverage, and I was already maxed out.</p>
<p>The other recurring deliverables were equally time-consuming:</p>
<table>
<thead>
<tr>
<th>Deliverable</th>
<th>Frequency</th>
<th>Manual time</th>
</tr>
</thead>
<tbody>
<tr>
<td>Ticker deep dive</td>
<td>8-12/week</td>
<td>2-3 hrs each</td>
</tr>
<tr>
<td>Sector scan (top 10 in sector)</td>
<td>2/week</td>
<td>3-4 hrs each</td>
</tr>
<tr>
<td>Weekly market summary</td>
<td>1/week</td>
<td>2-3 hrs</td>
</tr>
<tr>
<td>Earnings calendar + preview</td>
<td>1/week</td>
<td>1-2 hrs</td>
</tr>
<tr>
<td>Total</td>
<td></td>
<td>27-43 hrs/week</td>
</tr>
</tbody>
</table>
<p>Something had to change. I was choosing between growing the community and maintaining research quality, and that is a false choice when automation exists.</p>
<h2 id="the-build-architecture-and-tech-stack">The build: architecture and tech stack</h2>
<h3 id="core-components">Core components</h3>
<p>The system has four layers:</p>
<ol>
<li>Data layer: Finnhub API for market data (analyst ratings, earnings calendars, company profiles, sector breakdowns, insider transactions).</li>
<li>Analysis layer: Claude API for processing raw data into structured analysis, identifying patterns, calculating relative metrics, generating narrative insights.</li>
<li>Output layer: PDF generation for downloadable reports, Discord embed formatting for channel posts.</li>
<li>Delivery layer: Discord.py for automated posting to the correct channels with proper formatting, embeds, and file attachments.</li>
</ol>
<h3 id="data-pipeline-finnhub-integration">Data pipeline: Finnhub integration</h3>
<p>Finnhub is the backbone of the data layer. I chose it over alternatives because it has a generous free tier (60 API calls per minute), structured JSON responses that parse cleanly, and good coverage of analyst ratings and earnings data.</p>
<p>The bot pulls from these Finnhub endpoints:</p>
<table>
<thead>
<tr>
<th>Endpoint</th>
<th>Data</th>
<th>Use case</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>/stock/profile2</code></td>
<td>Company overview, sector, market cap</td>
<td>Context for every report</td>
</tr>
<tr>
<td><code>/stock/recommendation</code></td>
<td>Analyst buy/sell/hold consensus</td>
<td>Rating trends over time</td>
</tr>
<tr>
<td><code>/stock/earnings</code></td>
<td>Historical and estimated EPS</td>
<td>Earnings beat/miss tracking</td>
</tr>
<tr>
<td><code>/calendar/earnings</code></td>
<td>Upcoming earnings dates</td>
<td>Weekly calendar deliverable</td>
</tr>
<tr>
<td><code>/stock/insider-transactions</code></td>
<td>Insider buy/sell activity</td>
<td>Sentiment signal</td>
</tr>
<tr>
<td><code>/stock/peers</code></td>
<td>Related companies in sector</td>
<td>Peer comparison tables</td>
</tr>
<tr>
<td><code>/stock/metric</code></td>
<td>Financial ratios and metrics</td>
<td>Fundamental analysis</td>
</tr>
</tbody>
</table>
<p>Each ticker deep dive makes 7-9 API calls to Finnhub, taking roughly 8-12 seconds total including rate limiting. Compare that to 45-60 minutes of manual data gathering.</p>
<h3 id="analysis-layer-claude-api">Analysis layer: Claude API</h3>
<p>Raw data is not a research report. The analysis layer takes structured JSON from Finnhub and produces three things:</p>
<p>A quantitative summary with key metrics formatted as a comparison table (current values vs. sector averages, year-over-year changes).</p>
<p>A narrative analysis of 300-500 words covering the bull case, bear case, and key catalysts or risks. Written in plain language that a non-professional investor can understand.</p>
<p>And 2-3 watchlist items: specific things to monitor (upcoming earnings date, insider transaction pattern, analyst revision trend).</p>
<p>The prompt engineering here took the most iteration. Early versions produced generic analysis that could apply to any stock. After 3 weeks of tuning, the prompts now generate analysis that is specific to the ticker's situation, referencing actual numbers, identifying company-specific catalysts, and noting sector-relative positioning.</p>
<p>The quality benchmark I use: if I read the analysis and cannot tell which company it is about without seeing the ticker symbol, the prompt needs more work.</p>
<h3 id="output-layer-reports-and-embeds">Output layer: reports and embeds</h3>
<p>Every research deliverable gets produced in two formats simultaneously.</p>
<p>Discord embeds go out for immediate channel consumption. These use Discord's rich embed format, color-coded by sentiment (green for bullish, red for bearish, yellow for neutral), with inline fields for key metrics and a footer showing data freshness timestamp.</p>
<p>PDF reports go out for downloadable archives. These use a dark-themed design system with gradient accents and card layouts. Each PDF passes through a 5-gate verification check (existence, rendering integrity, content accuracy, visual quality, domain-specific validation) before delivery.</p>
<h3 id="delivery-layer-discordpy">Delivery layer: Discord.py</h3>
<p>The bot runs 24/7 on a VPS. It handles three types of delivery:</p>
<p>Scheduled posts go out on a fixed cadence. Weekly market summary every Monday at 8 AM. Earnings calendar preview every Sunday evening. Sector scans on Wednesday and Friday.</p>
<p>On-demand research lets community members type a ticker symbol in a designated channel, and the bot queues a deep dive. Results post within 15-20 minutes.</p>
<p>Alert-based posts fire when a stock on the community watchlist has a significant event: earnings beat/miss greater than 10%, analyst upgrade/downgrade, unusual insider activity. The bot posts an alert with context.</p>
<p>Rate limiting is built into every layer. Finnhub allows 60 calls per minute; the bot caps itself at 45 to leave headroom. Discord rate limits are handled with exponential backoff. During high-volume periods (earnings season), the bot queues requests and processes them in order rather than trying to parallelize beyond API limits.</p>
<h2 id="results-the-numbers-after-six-months">Results: the numbers after six months</h2>
<h3 id="time-savings">Time savings</h3>
<table>
<thead>
<tr>
<th>Deliverable</th>
<th>Manual time</th>
<th>Automated time</th>
<th>Savings</th>
</tr>
</thead>
<tbody>
<tr>
<td>Ticker deep dive (each)</td>
<td>2-3 hrs</td>
<td>12-15 min</td>
<td>90%</td>
</tr>
<tr>
<td>Sector scan</td>
<td>3-4 hrs</td>
<td>25-35 min</td>
<td>85%</td>
</tr>
<tr>
<td>Weekly market summary</td>
<td>2-3 hrs</td>
<td>15-20 min</td>
<td>88%</td>
</tr>
<tr>
<td>Earnings calendar</td>
<td>1-2 hrs</td>
<td>8-10 min</td>
<td>90%</td>
</tr>
<tr>
<td>Weekly total</td>
<td>27-43 hrs</td>
<td>3-5 hrs</td>
<td>87%</td>
</tr>
</tbody>
</table>
<p>The 3-5 hours I still spend are almost entirely review and editing. I read every report before it posts, adjust analysis that misses nuance, and add context from sources the bot does not access (management commentary on earnings calls, for example). The bot does the 85% of work that is data gathering and structuring. I handle the 15% that requires human judgment.</p>
<h3 id="output-volume-increase">Output volume increase</h3>
<p>Before the bot: 8-12 ticker deep dives per week, limited by my capacity.</p>
<p>After the bot: 15-20 ticker deep dives per week, limited only by community request volume. I also added two new deliverable types (earnings previews and alert-based posts) that I could not offer before because there was no bandwidth.</p>
<p>Total weekly output went from approximately 12 research pieces to 25-30. That is a 130% increase in volume with an 87% decrease in my time investment.</p>
<h3 id="accuracy-metrics">Accuracy metrics</h3>
<p>This was the metric I watched most closely. Automated analysis is only valuable if it is accurate.</p>
<p>The bot's earnings preview notes (bullish/bearish lean based on analyst consensus and recent trends) aligned with the actual earnings beat/miss direction 71% of the time over a 6-month sample of 180+ earnings events. For context, the analyst consensus itself is directionally correct roughly 65-70% of the time, so the bot slightly outperforms by synthesizing multiple data sources.</p>
<p>On data accuracy: zero instances of incorrect financial data in 6 months. Finnhub's structured API responses eliminate transcription errors that are common in manual research (copying the wrong number from a table, misreading a chart).</p>
<p>On analysis quality: I rate every report on a 1-5 scale before posting. The average score improved from 3.1 in month one to 4.2 by month six, reflecting prompt improvements and better data integration. Reports scoring below 3.5 get manual rewrites before posting. This happens roughly 8% of the time now, down from 31% in the first month.</p>
<h3 id="community-response">Community response</h3>
<p>The Discord community grew from 340 members to 680+ in the six months after deploying the bot. I cannot attribute all of that to the research automation since I was also producing more content on other platforms during the same period. But the community engagement metrics are suggestive:</p>
<ul>
<li>Average daily active users increased 45% (from 85 to 123)</li>
<li>Research channel engagement (reactions, replies, discussion) increased 62%</li>
<li>Member retention at 90 days improved from 54% to 71%</li>
</ul>
<p>The qualitative feedback was consistent: members valued the increased coverage (more tickers, more frequent scans) and the consistency (every report follows the same structure, posted on schedule). Several members specifically mentioned the earnings calendar and alert features as reasons they stayed.</p>
<h2 id="lessons-learned">Lessons learned</h2>
<h3 id="prompt-quality-is-everything">Prompt quality is everything</h3>
<p>The difference between a mediocre research report and a good one is entirely in the prompt. I spent more time tuning prompts (roughly 80 hours over 6 months) than I spent on any other aspect of the system. The data pipeline took 20 hours to build. The Discord delivery layer took 15 hours. The prompts took 80+ hours of iterative refinement.</p>
<p>Specific things that improved output quality:</p>
<ul>
<li>Including 3-4 example reports in the prompt as reference outputs</li>
<li>Explicitly instructing the model to reference specific numbers from the data (not generic statements like "revenue grew" but "revenue grew 14% year-over-year to $8.2B")</li>
<li>Adding a "what would a skeptical analyst question about this thesis" section to every report</li>
<li>Constraining report length to 300-500 words. Longer reports diluted the signal with filler.</li>
</ul>
<h3 id="build-the-verification-layer-before-you-need-it">Build the verification layer before you need it</h3>
<p>For the first three weeks, I posted reports without automated quality checks. In week two, a report went out with a data formatting error. An analyst rating was displayed as a raw float (3.67) instead of the intended "Strong Buy (3.67/5.0)." It was not a factual error, but it looked unprofessional and generated confused messages from members.</p>
<p>After that, I built a verification layer that checks every report before posting:</p>
<ul>
<li>All numerical values are properly formatted</li>
<li>Ticker symbol matches the company name</li>
<li>Report sections are present and non-empty</li>
<li>Sentiment color coding matches the analysis conclusion</li>
<li>PDF renders without broken characters or layout issues</li>
</ul>
<p>This catches 93% of formatting and accuracy issues before they reach the community. The cost of building it was about 12 hours. The cost of not having it was community trust erosion that takes much longer to repair.</p>
<h3 id="rate-limiting-needs-headroom">Rate limiting needs headroom</h3>
<p>I initially set the Finnhub API rate limit to 55 calls per minute (just under the 60/minute cap). During earnings season, when members were requesting 30+ tickers in rapid succession, the bot would occasionally hit the rate limit, get throttled, and produce incomplete reports with missing data sections.</p>
<p>The fix was simple: cap at 45 calls per minute (75% of the limit) and queue excess requests with estimated completion times posted to the channel. Members preferred knowing "your report will be ready in 8 minutes" over getting an incomplete report immediately. Headroom on rate limits is not wasted capacity. It is reliability insurance.</p>
<h3 id="separate-data-freshness-from-analysis-freshness">Separate data freshness from analysis freshness</h3>
<p>Early reports did not include timestamps on the underlying data. A report generated at 3 PM using data pulled at 9 AM could include an analyst rating that had been updated at noon. Members (reasonably) expected the numbers to be current.</p>
<p>The fix: every report now includes a "Data as of" timestamp in the footer, and the bot checks data freshness before analysis. If any critical data source is more than 4 hours stale, it re-pulls before generating the analysis. For time-sensitive deliverables (earnings day reports), the freshness window is 30 minutes.</p>
<h3 id="on-demand-is-a-better-model-than-batch">On-demand is a better model than batch</h3>
<p>My original design generated all reports in a batch overnight. The problem: by the time members logged in at market open, they wanted analysis on tickers that were not in the overnight batch (breaking news, pre-market movers). The batch was always one step behind.</p>
<p>Switching to an on-demand model where members request tickers and the bot generates reports in near-real-time increased satisfaction significantly. I still run scheduled deliverables (weekly summary, earnings calendar) on a batch schedule, but the core ticker research is on-demand. This also reduced wasted work: the batch model generated reports for tickers nobody was watching that week.</p>
<h2 id="what-i-would-build-differently">What I would build differently</h2>
<p>If I started this project over today, three things would change.</p>
<p>I would add more data sources. Finnhub is solid for fundamentals and analyst data, but it does not cover options flow, social sentiment, or macroeconomic indicators. Integrating 2-3 additional data sources (options data via CBOE, sentiment via social aggregators, macro via FRED) would make the analysis meaningfully more comprehensive. I estimate this would take 40-50 hours of integration work.</p>
<p>I would invest in better charting. The current reports use text-based tables for data presentation. Interactive or even static charts (price trend, earnings history, analyst rating trajectory) would improve readability significantly. Discord embeds support images, and I could generate chart images programmatically using matplotlib or Plotly. This is on the roadmap but not yet built.</p>
<p>I would add tiered access. Currently every community member gets the same reports. A tiered model where free members get weekly summaries and premium members get on-demand deep dives and alerts would create a revenue stream from the community itself. The bot architecture supports this (role-based channel access in Discord), but the content strategy is not yet implemented.</p>
<h2 id="the-broader-pattern">The broader pattern</h2>
<p>This stock research bot is one agent in a <a href="/blog/multi-agent-system-case-study">larger multi-agent system</a> that manages six business functions. The pattern is the same across all of them:</p>
<ol>
<li>Identify the manual workflow that consumes the most time</li>
<li>Map the data sources, analysis steps, and delivery format</li>
<li>Build the pipeline: data in, analysis in the middle, formatted output at the end</li>
<li>Add verification before delivery</li>
<li>Tune prompts and workflows based on output quality metrics</li>
<li>Iterate for 4-8 weeks until quality stabilizes</li>
</ol>
<p>The stock research bot was the first agent I built, and it taught me the framework I used for every agent that followed. If you are considering building something similar for your own domain, our <a href="/services/automation-audit">automation audit</a> can help you identify the right starting point.</p>
<h2 id="frequently-asked-questions">Frequently asked questions</h2>
<h3 id="how-much-does-the-stock-research-bot-cost-to-run-monthly">How much does the stock research bot cost to run monthly?</h3>
<p>Approximately $80-120/month. Finnhub's premium tier is $50/month for real-time data and higher rate limits. Claude API usage for 80-100 research reports per month runs $20-50 depending on report complexity. VPS hosting for the Discord bot is $10-15/month. Compare that to the 18-20 hours per week of manual research time it replaced.</p>
<h3 id="can-this-approach-work-for-crypto-or-forex-research">Can this approach work for crypto or forex research?</h3>
<p>The architecture is the same; only the data sources change. Finnhub covers some crypto data, but you would likely swap in specialized APIs (CoinGecko for crypto, OANDA or Alpha Vantage for forex). The analysis layer and delivery layer are completely reusable. A crypto or forex adaptation would take approximately 30-40 hours of integration work, mostly in connecting new data sources and tuning prompts for domain-specific analysis patterns.</p>
<h3 id="how-do-you-handle-the-bot-giving-bad-stock-analysis">How do you handle the bot giving bad stock analysis?</h3>
<p>Every report includes a disclaimer that this is automated analysis, not financial advice. More practically, I review every report before it posts to the community. Reports that miss important context or draw weak conclusions get flagged for manual editing. The verification layer catches data formatting errors automatically, but analytical quality still requires human judgment. My quality review adds about 2-3 hours per week to the process, but it is the most important 2-3 hours I spend.</p>
<h3 id="what-happens-during-earnings-season-when-volume-spikes">What happens during earnings season when volume spikes?</h3>
<p>Earnings season (January, April, July, October) typically doubles request volume. The bot handles this through a queuing system where requests are processed in order with estimated wait times posted to the channel. During peak periods, wait times can reach 15-20 minutes per report instead of the usual 12-15 minutes. I also pre-generate earnings preview reports for the most-watched tickers 24 hours before their reporting date, which reduces on-demand load during the busiest hours. Check our <a href="/pricing">pricing page</a> for how we structure research automation services for investment communities.</p>