DEV Community

shakti tiwari
shakti tiwari

Posted on

Building a Telegram Bot for NSE Alerts on Android (No Server Required)

Building a Telegram Bot for NSE Alerts on Android (No Server Required)

DOYR | Not financial/legal/tax advice. For educational purposes only.


You're not at your desk. You're on a bus, or at a café, or just away from your laptop.

And Nifty just broke a key level. Or TCS gapped up 3% on earnings. Or your stock screener found a perfect setup.

How do you get the alert instantly?

Not email. Not a website refresh. Not a push notification that takes 5 minutes to arrive.

Telegram.

Telegram is perfect for trading alerts because:

  • Instant delivery (1-2 seconds)
  • Works on Android (no server needed)
  • Free (no SMS charges, no email limits)
  • Rich formatting (bold, charts, tables)
  • Group support (share with friends)
  • API is simple (10 lines of code)

This guide shows you how to build a complete Telegram bot for NSE alerts that runs on your Android phone via Termux. No cloud server. No VPS. No ₹500/month hosting.

Just your phone. Python. Telegram.


What You'll Build

Feature Function
Price alerts Nifty crosses key levels
Screener alerts Stock screener finds matches
News alerts Important NSE news headlines
Performance alerts Portfolio P&L updates
Daily brief Morning market summary
Custom filters Your rules, your alerts

Cost: ₹0
Time: 1 hour
Platform: Termux/Android


Step 1: Create Telegram Bot

1.1 Get Bot Token from BotFather

  1. Open Telegram → Search @botfather
  2. Send /newbot
  3. Name your bot: Shakti Trading Bot
  4. Username: shakti_nse_bot (must end in bot)
  5. BotFather sends you a token: 123456789:ABCdefGhIJKlmNoPQRsTUVwxyz

Save this token. You'll need it.

1. Get Your Chat ID

  1. Search @userinfobot on Telegram
  2. Send /start
  3. It replies with your Chat ID: 123456789

Save this too.


Step 2: Install Dependencies on Termux

# Update Termux
pkg update -y && pkg upgrade -y

# Install Python + libraries
pkg install python python-dev pip -y
pip install requests python-telegram-bot==20.7 yfinance pandas schedule

# Verify
python -c "import telegram; print('Telegram bot ready')"
Enter fullscreen mode Exit fullscreen mode

Key libraries:
| Library | Purpose |
|---------|---------|
| requests | Fetch NSE data |
| python-telegram-bot | Telegram API wrapper |
| yfinance | Stock data fallback |
| pandas | Data processing |
| schedule | Job scheduling |


Step 3: Basic Bot Structure

from telegram import Bot
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
import os

# Configuration
TOKEN = "YOUR_BOT_TOKEN_HERE"
CHAT_ID = "YOUR_CHAT_ID_HERE"

class NSEAlertBot:
    def __init__(self):
        self.bot = Bot(token=TOKEN)
        self.chat_id = CHAT_ID

    def send_message(self, text, parse_mode='Markdown'):
        """Send message to Telegram"""
        try:
            self.bot.send_message(
                chat_id=self.chat_id,
                text=text,
                parse_mode=parse_mode
            )
            print(f"✅ Alert sent: {text[:50]}...")
        except Exception as e:
            print(f"❌ Failed to send: {e}")

    def send_photo(self, photo_path, caption=None):
        """Send chart/image to Telegram"""
        try:
            with open(photo_path, 'rb') as photo:
                self.bot.send_photo(
                    chat_id=self.chat_id,
                    photo=photo,
                    caption=caption
                )
            print(f"✅ Photo sent: {photo_path}")
        except Exception as e:
            print(f"❌ Failed to send photo: {e}")

# Test
bot = NSEAlertBot()
bot.send_message("🧪 *Test Alert*\n\nBot is working! Ready for NSE alerts.")
Enter fullscreen mode Exit fullscreen mode

Run test:

python nse_bot.py
Enter fullscreen mode Exit fullscreen mode

You should see: "🧪 Test Alert" in your Telegram.


Step 4: Price Alert System

import yfinance as yf
import time

class PriceAlertBot(NSEAlertBot):
    def __init__(self):
        super().__init__()
        self.alerts = []

    def add_alert(self, symbol, condition, target_price):
        """
        Add price alert
        condition: 'above' or 'below'
        """
        self.alerts.append({
            'symbol': symbol,
            'condition': condition,
            'target': target_price,
            'triggered': False
        })

    def check_alerts(self):
        """Check all alerts against current prices"""
        triggered = []

        for alert in self.alerts:
            if alert['triggered']:
                continue

            try:
                # Fetch current price
                ticker = f"{alert['symbol']}.NS"
                stock = yf.Ticker(ticker)
                hist = stock.history(period="1d")

                if len(hist) == 0:
                    continue

                current_price = hist['Close'].iloc[-1]

                # Check condition
                if alert['condition'] == 'above' and current_price >= alert['target']:
                    alert['triggered'] = True
                    triggered.append({
                        'symbol': alert['symbol'],
                        'condition': f"rose above ₹{alert['target']}",
                        'current': current_price,
                        'alert': alert
                    })

                elif alert['condition'] == 'below' and current_price <= alert['target']:
                    alert['triggered'] = True
                    triggered.append({
                        'symbol': alert['symbol'],
                        'condition': f"fell below ₹{alert['target']}",
                        'current': current_price,
                        'alert': alert
                    })

            except Exception as e:
                print(f"Error checking {alert['symbol']}: {e}")

        return triggered

    def process_alerts(self):
        """Check alerts and send Telegram notifications"""
        triggered = self.check_alerts()

        for t in triggered:
            emoji = "🔴" if "fell" in t['condition'] else "🟢"
            message = f"""{emoji} *PRICE ALERT*

{t['symbol']} has {t['condition']}
Current price: ₹{t['current']:.2f}

Action: {'Consider selling' if 'fell' in t['condition'] else 'Consider buying'}"""

            self.send_message(message)

# Usage
bot = PriceAlertBot()

# Add alerts
bot.add_alert('NIFTY', 'above', 22500)
bot.add_alert('NIFTY', 'below', 22000)
bot.add_alert('TCS', 'above', 4500)
bot.add_alert('RELIANCE', 'below', 2900)

# Check every 5 minutes during market hours
while True:
    now = datetime.now()
    if now.hour >= 9 and now.hour <= 15:  # Market hours
        bot.process_alerts()
    time.sleep(300)  # Check every 5 minutes
Enter fullscreen mode Exit fullscreen mode

Step 5: Stock Screener Alerts

class ScreenerAlertBot(NSEAlertBot):
    def __init__(self):
        super().__init__()
        self.screener_results = []

    def send_screener_results(self, results_df):
        """Send screener results to Telegram"""
        if results_df.empty:
            message = "📊 *Daily Screener Results*\n\nNo stocks matched criteria today."
        else:
            message = f"📊 *Daily Screener Results*\n\n"
            message += f"Found {len(results_df)} stocks:\n\n"

            for _, row in results_df.iterrows():
                message += f"*{row['symbol']}* ({row.get('name', '')})\n"
                message += f"Price: ₹{row['close']}\n"
                message += f"Change: {row.get('change_pct', 0)}%\n"
                message += f"RSI: {row.get('rsi', 'N/A')}\n"
                message += f"Momentum: {row.get('momentum', 'N/A')}%\n\n"

        self.send_message(message)

    def send_top_pick(self, row):
        """Send top pick with detailed analysis"""
        message = f"""⭐ *TOP PICK: {row['symbol']}*

Price: ₹{row['close']}
Change: {row.get('change_pct', 0)}%
RSI: {row.get('rsi', 'N/A')}
Momentum: {row.get('momentum', 'N/A')}%
Volume Ratio: {row.get('volume_ratio', 'N/A')}x

Score: {row.get('score', 'N/A')}/100

Action: Consider buying at market price
Stop Loss: -5%
Target: +10%"""

        self.send_message(message)

# Usage
screener_bot = ScreenerAlertBot()

# After running screener
results = screener.screen_nifty50()
screener_bot.send_screener_results(results)

# Send top pick
top_pick = results.iloc[0]
screener_bot.send_top_pick(top_pick)
Enter fullscreen mode Exit fullscreen mode

Step 6: News Alert System

class NewsAlertBot(NSEAlertBot):
    def __init__(self):
        super().__init__()
        self.keywords = [
            'Nifty', 'Sensex', 'banking', 'IT stocks', 'RBI',
            'SEBI', 'FII', 'budget', 'earnings', 'merger'
        ]

    def fetch_nse_news(self):
        """Fetch latest NSE news"""
        url = "https://www.nseindia.com/api/latest-circulars"
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
        }

        try:
            import requests
            response = requests.get(url, headers=headers, timeout=10)
            data = response.json()

            headlines = []
            for item in data.get('data', [])[:10]:
                headlines.append({
                    'title': item.get('subject', ''),
                    'date': item.get('date', ''),
                    'url': item.get('link', '')
                })

            return headlines
        except Exception as e:
            print(f"Failed to fetch news: {e}")
            return []

    def filter_important_news(self, headlines):
        """Filter news based on keywords"""
        important = []

        for headline in headlines:
            for keyword in self.keywords:
                if keyword.lower() in headline['title'].lower():
                    important.append(headline)
                    break

        return important

    def send_news_alert(self):
        """Send important news to Telegram"""
        headlines = self.fetch_nse_news()
        important = self.filter_important_news(headlines)

        if not important:
            return

        message = "📰 *Important NSE News*\n\n"

        for i, news in enumerate(important[:5], 1):
            message += f"{i}. *{news['title']}*\n"
            message += f"   Date: {news['date']}\n\n"

        self.send_message(message)

# Usage
news_bot = NewsAlertBot()
news_bot.send_news_alert()
Enter fullscreen mode Exit fullscreen mode

Step 7: Portfolio P&L Alerts

class PortfolioAlertBot(NSEAlertBot):
    def __init__(self, portfolio):
        super().__init__()
        self.portfolio = portfolio  # List of {symbol, qty, buy_price}

    def calculate_pnl(self):
        """Calculate portfolio P&L"""
        total_value = 0
        total_cost = 0
        pnl_details = []

        for stock in self.portfolio:
            try:
                ticker = f"{stock['symbol']}.NS"
                data = yf.Ticker(ticker)
                hist = data.history(period="1d")

                if len(hist) == 0:
                    continue

                current_price = hist['Close'].iloc[-1]
                current_value = current_price * stock['qty']
                cost = stock['buy_price'] * stock['qty']
                pnl = current_value - cost
                pnl_pct = (current_price - stock['buy_price']) / stock['buy_price'] * 100

                total_value += current_value
                total_cost += cost

                pnl_details.append({
                    'symbol': stock['symbol'],
                    'qty': stock['qty'],
                    'buy_price': stock['buy_price'],
                    'current': current_price,
                    'pnl': pnl,
                    'pnl_pct': pnl_pct
                })

            except Exception as e:
                print(f"Error calculating {stock['symbol']}: {e}")

        total_pnl = total_value - total_cost
        total_pnl_pct = (total_pnl / total_cost * 100) if total_cost > 0 else 0

        return {
            'total_value': total_value,
            'total_cost': total_cost,
            'total_pnl': total_pnl,
            'total_pnl_pct': total_pnl_pct,
            'stocks': pnl_details
        }

    def send_portfolio_alert(self):
        """Send portfolio P&L to Telegram"""
        pnl = self.calculate_pnl()

        emoji = "🟢" if pnl['total_pnl'] > 0 else "🔴"

        message = f"""{emoji} *Portfolio Update*

Total Value: ₹{pnl['total_value']:,.0f}
Total P&L: ₹{pnl['total_pnl']:,.0f} ({pnl['total_pnl_pct']:.1f}%)

*Stock-wise:*
"""

        for stock in pnl['stocks']:
            emoji_stock = "🟢" if stock['pnl'] > 0 else "🔴"
            message += f"\n{emoji_stock} *{stock['symbol']}*: "
            message += f"{stock['pnl']:,.0f} ({stock['pnl_pct']:.1f}%)"

        self.send_message(message)

# Usage
portfolio = [
    {'symbol': 'TCS', 'qty': 10, 'buy_price': 4200},
    {'symbol': 'INFY', 'qty': 20, 'buy_price': 1550},
    {'symbol': 'HDFC', 'qty': 15, 'buy_price': 1650}
]

portfolio_bot = PortfolioAlertBot(portfolio)
portfolio_bot.send_portfolio_alert()
Enter fullscreen mode Exit fullscreen mode

Step 8: Schedule Daily Brief

from schedule import every, repeat, run_pending
import time

class DailyBriefBot(NSEAlertBot):
    def send_morning_brief(self):
        """Send morning market brief"""
        # Fetch Nifty data
        nifty = yf.Ticker("^NSEI")
        hist = nifty.history(period="2d")

        if len(hist) >= 2:
            prev_close = hist['Close'].iloc[-2]
            current = hist['Close'].iloc[-1]
            change = current - prev_close
            change_pct = (change / prev_close) * 100

            message = f"""☀️ *Good Morning! Market Brief*

*Nifty 50:*
- Previous Close: ₹{prev_close:,.0f}
- Current: ₹{current:,.0f}
- Change: ₹{change:,.0f} ({change_pct:+.2f}%)

*Global Cues:*
- US markets: {'Up' if change > 0 else 'Down'}
- Asian markets: Mixed
- VIX: {hist['Close'].iloc[-1] * 100:.1f}

*Today's Focus:*
- Key levels: 21,800 - 22,200
- Watch: TCS earnings, RBI policy
- Sentiment: {'Bullish' if change > 0 else 'Bearish'}

*Quote of the day:*
"The stock market is a device for transferring money from the impatient to the patient." — Buffett

Good luck trading today! 📈"""

            self.send_message(message)

    def send_eod_summary(self):
        """Send end-of-day summary"""
        nifty = yf.Ticker("^NSEI")
        hist = nifty.history(period="1d")

        if len(hist) > 0:
            close = hist['Close'].iloc[-1]
            high = hist['High'].iloc[-1]
            low = hist['Low'].iloc[-1]

            message = f"""🌙 *Market Closed*

*Nifty 50:*
- Close: ₹{close:,.0f}
- High: ₹{high:,.0f}
- Low: ₹{low:,.0f}
- Range: {((high - low) / low * 100):.1f}%

*Tomorrow's Plan:*
- Review today's trades
- Check FII/DII data
- Run screener
- Set alerts for key levels

Rest up. Markets reopen at 9:15 AM. 🌅"""

            self.send_message(message)

# Schedule
brief_bot = DailyBriefBot()

# Morning brief at 8:45 AM
@repeat(every().day.at("08:45"))
def morning_brief():
    brief_bot.send_morning_brief()

# EOD summary at 3:45 PM
@repeat(every().day.at("15:45"))
def eod_summary():
    brief_bot.send_eod_summary()

# Run scheduler
while True:
    run_pending()
    time.sleep(60)
Enter fullscreen mode Exit fullscreen mode

Step 9: Advanced Features

9.1 Interactive Commands

from telegram.ext import CommandHandler

def start(update, context):
    """Handle /start command"""
    welcome_message = """👋 Welcome to Shakti Trading Bot!

Commands:
/start - Show this message
/price SYMBOL - Get current price (e.g., /price TCS)
/portfolio - Show portfolio P&L
/alerts - List active alerts
/brief - Get morning brief
/news - Get latest NSE news
/help - Show help

Example: /price TCS"""
    update.message.reply_text(welcome_message)

def price_command(update, context):
    """Handle /price command"""
    if len(context.args) == 0:
        update.message.reply_text("Usage: /price SYMBOL\nExample: /price TCS")
        return

    symbol = context.args[0].upper()
    try:
        ticker = f"{symbol}.NS"
        stock = yf.Ticker(ticker)
        hist = stock.history(period="1d")

        if len(hist) > 0:
            price = hist['Close'].iloc[-1]
            change = hist['Close'].iloc[-1] - hist['Open'].iloc[0]
            change_pct = (change / hist['Open'].iloc[0]) * 100

            emoji = "🟢" if change > 0 else "🔴"
            message = f"{emoji} *{symbol}*\n\n"
            message += f"Price: ₹{price:.2f}\n"
            message += f"Change: ₹{change:+.2f} ({change_pct:+.2f}%)"

            update.message.reply_text(message, parse_mode='Markdown')
        else:
            update.message.reply_text(f"No data found for {symbol}")
    except Exception as e:
        update.message.reply_text(f"Error: {e}")

def portfolio_command(update, context):
    """Handle /portfolio command"""
    portfolio_bot = PortfolioAlertBot(PORTFOLIO)
    pnl = portfolio_bot.calculate_pnl()

    emoji = "🟢" if pnl['total_pnl'] > 0 else "🔴"
    message = f"{emoji} *Portfolio P&L*\n\n"
    message += f"Total: ₹{pnl['total_pnl']:,.0f} ({pnl['total_pnl_pct']:+.1f}%)\n\n"

    for stock in pnl['stocks']:
        emoji_s = "🟢" if stock['pnl'] > 0 else "🔴"
        message += f"{emoji_s} {stock['symbol']}: ₹{stock['pnl']:,.0f}\n"

    update.message.reply_text(message, parse_mode='Markdown')

# Register handlers
updater = Updater(TOKEN)
dispatcher = updater.dispatcher

dispatcher.add_handler(CommandHandler("start", start))
dispatcher.add_handler(CommandHandler("price", price_command))
dispatcher.add_handler(CommandHandler("portfolio", portfolio_command))

# Start bot
updater.start_polling()
updater.idle()
Enter fullscreen mode Exit fullscreen mode

9.2 Chart Generation

import matplotlib.pyplot as plt

class ChartBot(NSEAlertBot):
    def send_price_chart(self, symbol, days=30):
        """Generate and send price chart"""
        ticker = f"{symbol}.NS"
        stock = yf.Ticker(ticker)
        hist = stock.history(period=f"{days}d")

        if len(hist) == 0:
            return

        # Create chart
        plt.figure(figsize=(10, 6))
        plt.plot(hist.index, hist['Close'], color='blue', linewidth=2)
        plt.title(f"{symbol} - Last {days} Days")
        plt.xlabel('Date')
        plt.ylabel('Price (₹)')
        plt.grid(True, alpha=0.3)
        plt.tight_layout()

        # Save
        chart_path = f"/tmp/{symbol}_chart.png"
        plt.savefig(chart_path, dpi=100)
        plt.close()

        # Send
        self.send_photo(chart_path, caption=f"{symbol} price chart - {days} days")

        # Clean up
        os.remove(chart_path)

# Usage
chart_bot = ChartBot()
chart_bot.send_price_chart('TCS', days=30)
Enter fullscreen mode Exit fullscreen mode

Step 10: Run on Termux as Background Service

Method 1: Termux:Boot + Service

# Install Termux:Boot app from F-Droid
# This allows auto-start on boot

# Create service script
cat > ~/nse_bot/nse_bot_service.sh << 'EOF'
#!/data/data/com.termux/files/home/usr/bin/bash
cd ~/nse_bot
python nse_bot.py >> logs/bot.log 2>&1
EOF

chmod +x ~/nse_bot/nse_bot_service.sh
Enter fullscreen mode Exit fullscreen mode

Method 2: Cron Jobs (Recommended)

# Edit crontab
crontab -e

# Add these lines:

# Morning brief at 8:45 AM
45 8 * * 1-5 python ~/nse_bot/daily_brief.py >> ~/nse_bot/logs/brief.log 2>&1

# Price alerts every 5 min during market hours
*/5 9-15 * * 1-5 python ~/nse_bot/price_alerts.py >> ~/nse_bot/logs/alerts.log 2>&1

# EOD summary at 3:45 PM
45 15 * * 1-5 python ~/nse_bot/eod_summary.py >> ~/nse_bot/logs/eod.log 2>&1

# News alerts every 30 min
*/30 9-15 * * 1-5 python ~/nse_bot/news_alerts.py >> ~/nse_bot/logs/news.log 2>&1
Enter fullscreen mode Exit fullscreen mode

Method 3: Keep Alive with Termux Widget

# Install Termux:Widget
# Add widget to home screen
# Point to script:
~/nse_bot/run_bot.sh
Enter fullscreen mode Exit fullscreen mode

Complete Bot Code (Copy-Paste)

File: nse_bot_complete.py

from telegram import Bot
from telegram.ext import Updater, CommandHandler
import yfinance as yf
import pandas as pd
import time
from datetime import datetime
import os

TOKEN = "YOUR_TOKEN"
CHAT_ID = "YOUR_CHAT_ID"

class CompleteNSEBot:
    def __init__(self):
        self.bot = Bot(token=TOKEN)
        self.chat_id = CHAT_ID
        self.alerts = []

    def send_message(self, text):
        self.bot.send_message(chat_id=self.chat_id, text=text, parse_mode='Markdown')

    def price_alert(self, symbol, condition, target):
        self.alerts.append({'symbol': symbol, 'condition': condition, 'target': target})

    def check_alerts(self):
        for alert in self.alerts:
            if alert.get('triggered'):
                continue
            try:
                data = yf.Ticker(f"{alert['symbol']}.NS")
                hist = data.history(period="1d")
                if len(hist) == 0:
                    continue
                price = hist['Close'].iloc[-1]

                if alert['condition'] == 'above' and price >= alert['target']:
                    alert['triggered'] = True
                    self.send_message(f"🔔 {alert['symbol']} crossed ₹{alert['target']}!\nCurrent: ₹{price:.2f}")
                elif alert['condition'] == 'below' and price <= alert['target']:
                    alert['triggered'] = True
                    self.send_message(f"🔔 {alert['symbol']} fell below ₹{alert['target']}!\nCurrent: ₹{price:.2f}")
            except:
                continue

    def run(self):
        """Main loop"""
        while True:
            now = datetime.now()
            if now.hour >= 9 and now.hour <= 15:
                self.check_alerts()
            time.sleep(300)  # 5 minutes

if __name__ == "__main__":
    bot = CompleteNSEBot()

    # Add your alerts
    bot.price_alert('NIFTY', 'above', 22500)
    bot.price_alert('NIFTY', 'below', 22000)
    bot.price_alert('TCS', 'above', 4500)

    bot.run()
Enter fullscreen mode Exit fullscreen mode

FAQ

Q1: Is Telegram bot free?

A: Yes. Telegram API is completely free. No limits on messages.

Q2: Can I run this without Termux?

A: Yes. Any Python environment works. Termux is just convenient for Android.

Q3: Do I need a server?

A: No. Your phone runs the bot 24/7 via Termux + cron.

Q4: What if my phone is off?

A: Bot stops. For 24/7 uptime, use a cheap VPS (~₹200/month) or Raspberry Pi.

Q5: Can I add multiple users?

A: Yes. Replace single CHAT_ID with a list. Send to all.

Q6: Is this secure?

A: Bot token = password. Don't share it. Use environment variables.

Q7: Can I send charts?

A: Yes. Use matplotlib to generate charts, send via send_photo().

Q8: What's the message limit?

A: Telegram allows ~30 messages/second per bot. Way more than you need.


The Bottom Line

A Telegram bot for NSE alerts gives you:

  • Instant notifications (1-2 seconds)
  • Zero cost (free API, no server)
  • Works on Android (Termux)
  • Customizable (your rules, your alerts)
  • Scalable (add more features anytime)

This is what I use in nse_ai_agent. And now you can build it too.


Connect With Shakti Tiwari


Published on Dev.to | Tags: #telegram #nse #trading #bot #python #android #termux


Author

Shakti Tiwari is an AI/quant trader and open-source developer from Chandigarh. He builds free trading tools for Indian retail traders and writes about NSE markets, Python, and AI in finance. Author of Right Brain Wins and Brain Markets.

Top comments (0)