Lessons Learnt Building a Trading Bot with Python and Telegram
Weekend projects have a way of getting serious. IfexTraderBot started as a Friday afternoon experiment — can I pull NGX closing prices and send them to a Telegram group automatically? Twelve months later it’s a production system with scheduled jobs, a Redis cache, Azure Functions, a FastAPI backend, and several hundred active users.
Here’s what I learnt, what I got wrong, and what I would do differently.
The Stack I Chose (and Why)
Python was an obvious choice for the signal engine. The financial data libraries (pandas, ta-lib, yfinance) are Python-first. I’ve written more C# than Python in my career, but for this domain, Python is the right tool.
Telegram Bot API over WhatsApp or a custom app. Nigerian retail investors are already in Telegram. The Bot API is mature, well-documented, and handles message delivery, inline keyboards, and group management elegantly. Building a custom app would have meant convincing people to download something — Telegram skips that entirely.
FastAPI for the backend API. I considered Django but didn’t need the ORM or admin — I needed a fast, typed API that I could iterate on quickly. FastAPI with Pydantic gives you that.
Redis for the alert engine. When a price crosses a threshold, the alert needs to deliver within seconds, not minutes. A PostgreSQL polling loop would have worked but added latency. Redis pub/sub gives the low-latency delivery we needed.
Azure Functions for scheduled jobs. Market open (9:30 AM), market close (2:30 PM), daily P&L summary (6 PM), weekly briefing (Monday 7 AM). Azure Functions with timer triggers handle this cleanly without managing a cron server.
Mistake 1: Treating the Bot as a Stateless Interface
Early versions of IfexTraderBot were stateless — every command was a complete round trip to the database. This worked fine for 50 users. At 300 users, the database started showing load, and commands were taking 2–4 seconds to respond.
The fix was obvious in retrospect: cache user state in Redis. A user’s portfolio, alert preferences, and settings are loaded into Redis when they first interact and updated on write. Database reads drop by ~80%. Response times go back under 500ms.
Lesson: Don’t treat a chat bot like a web page. Users expect instant responses. The UX expectation is messaging, not web browsing.
Mistake 2: Synchronous Signal Processing
The signal engine processes price data and generates signals. Version 1 ran this synchronously — when a scheduled job triggered, it processed all instruments sequentially. With 50 instruments it took 8 seconds. With 200 instruments it would have taken 32 seconds — longer than the Azure Function timeout.
The fix: async processing with asyncio and task queues. Instruments are processed in parallel batches. Total processing time for 200 instruments dropped to under 3 seconds.
This is a general Python lesson: if you’re doing I/O-bound work (API calls, database queries) in a loop, you’re leaving significant performance on the table.
Mistake 3: No Graceful Degradation for Data Feeds
The NGX data feed I was using went down twice in the first month. Both times the bot sent no signals and users assumed it was broken. One user publicly complained in a Telegram group. I found out via a screenshot forwarded to me by a friend.
The fix: explicit status messages and data source fallbacks. When the primary data feed is unavailable, the bot sends a message: “NGX data feed is delayed — signals will resume when data is available. Last update: 14:32.” Users know the bot is working, just waiting for data.
A silent failure is always worse than a noisy one.
Mistake 4: No Rate Limiting on Commands
Telegram’s Bot API has rate limits. If you send too many messages to a group too quickly, your bot gets temporarily muted. I hit this on a day when there was significant NGX volatility and the alert engine fired 200 alerts in 30 seconds.
The fix: a message queue with rate limiting. Alerts are enqueued. A consumer sends at maximum 25 messages per second per chat. The Telegram API never sees a burst.
What I’d Do Differently
Start with a proper message queue from day one. I used Redis pub/sub as a quick fix that became permanent. I should have started with a proper queue (Azure Service Bus or even a Redis Stream) with explicit consumer groups and dead-letter handling.
Write integration tests that use the real Telegram API. My test suite mocked the Telegram API, which meant I caught logic errors but not API behaviour edge cases. Real integration tests (against a test bot) would have caught the rate limiting issue before production.
Design the data model for multi-asset from the start. The database schema started as “NGX equities only.” Extending it to forex and crypto required a migration I could have avoided with more foresight.
What Worked Well
The FastAPI + Pydantic combination was excellent for rapid iteration. Type errors caught at startup, not at runtime. Clear error messages. Easy to add new endpoints.
Azure Functions timer triggers are genuinely delightful for scheduled jobs. Define the schedule in a cron expression, write the function, deploy. No server management, no cron daemon, no worrying about overlap.
Building in public — sharing progress in the Telegram community early generated a small but highly engaged group of early users who gave honest, detailed feedback.
The Code
IfexTraderBot is not open source yet. But if you’re building something similar — a notification bot, a signal system, anything that pushes time-sensitive data to users via Telegram — feel free to reach out. I’m happy to talk architecture.