Skip to content

Harsh-Codes-77/StockWatcher

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

📈 StockWatcher — CANSLIM Telegram Alert Bot

A lightweight Python bot that monitors a Screener.in CANSLIM screen and sends real-time Telegram alerts whenever a new stock enters the screen.


Table of Contents


How It Works

The bot runs a continuous loop with two jobs happening in parallel:

  1. Telegram Poller — Checks every 3 seconds for new /start messages. Any user who sends /start gets added to the subscriber list.

  2. Screener Monitor — Every 30 minutes, logs into Screener.in, scrapes all stocks from your CANSLIM screen (across all pages), and compares with the previously saved list. If any new stock has appeared, it broadcasts an alert to all subscribers via Telegram.

State (subscriber list, last stock list, Telegram update offset) is persisted in Upstash Redis if configured, otherwise falls back to local JSON files.


Architecture

┌─────────────────────────────────────────────────────────────┐
│                        watcher.py                           │
│                                                             │
│  ┌──────────────────┐         ┌──────────────────────────┐  │
│  │  Telegram Poller │         │   Screener Monitor       │  │
│  │  (every 3 sec)   │         │   (every 30 min)         │  │
│  │                  │         │                          │  │
│  │  getUpdates API  │         │  Login → Scrape Pages    │  │
│  │  /start → add    │         │  Compare with last run   │  │
│  │  subscriber      │         │  Broadcast if new stocks │  │
│  └────────┬─────────┘         └──────────┬───────────────┘  │
│           │                              │                  │
│           └──────────┬───────────────────┘                  │
│                      │                                      │
│              ┌───────▼──────────┐                           │
│              │   State Layer    │                           │
│              │                  │                           │
│              │  Upstash Redis   │                           │
│              │  (or local JSON) │                           │
│              └──────────────────┘                           │
└─────────────────────────────────────────────────────────────┘

Flow Diagrams

Main Loop

flowchart TD
    A([Bot Starts]) --> B[Validate env vars]
    B --> C[Broadcast LIVE message to all subscribers]
    C --> D{Main Loop}

    D --> E[Process Telegram Updates]
    E --> F{New /start message?}
    F -- Yes --> G[Add chat_id to subscribers]
    G --> H[Send welcome message]
    H --> D
    F -- No --> I{30 min elapsed?}

    I -- No --> J[Sleep 3 seconds]
    J --> D

    I -- Yes --> K[Fetch stocks from Screener.in]
    K --> L[Load last known stock list]
    L --> M{New stocks found?}

    M -- Yes --> N[Broadcast CANSLIM ALERT to all subscribers]
    N --> O[Save updated stock list]
    O --> P[Reset 30-min timer]
    P --> D

    M -- No --> Q[Log: No new stocks]
    Q --> P
Loading

Screener Scraping Flow

flowchart TD
    A([get_stocks_from_screener called]) --> B{Session exists?}
    B -- No --> C[Login to Screener.in]
    C --> D[Fetch CSRF token from login page]
    D --> E[POST credentials]
    E --> F{Login successful?}
    F -- No --> G([Raise exception])
    F -- Yes --> H[Store session]

    B -- Yes --> H
    H --> I[Fetch page 1]
    I --> J{Redirected to login?}
    J -- Yes --> K{Already retried?}
    K -- No --> L[Reset session, retry login]
    L --> I
    K -- Yes --> M([Skip this cycle])

    J -- No --> N[Parse all stock names from td.text]
    N --> O{More pages?}
    O -- Yes --> P[Increment page, sleep 2s]
    P --> I
    O -- No --> Q([Return full stock list])
Loading

Subscriber Management Flow

flowchart TD
    A([process_telegram_updates called]) --> B[GET /getUpdates with offset]
    B --> C{Response OK?}
    C -- No --> Z([Return silently])
    C -- Yes --> D{Any updates?}
    D -- No --> Z
    D -- Yes --> E[Loop through updates]

    E --> F{Message is /start?}
    F -- No --> G[Skip update]
    F -- Yes --> H{chat_id already subscribed?}

    H -- Yes --> I[Send: Already subscribed]
    H -- No --> J[Add to subscriber list]
    J --> K[Save subscribers to Redis/JSON]
    K --> L[Send welcome message]

    L --> M[Update offset to update_id + 1]
    M --> N[Save offset]
    N --> E
Loading

State Persistence Flow

flowchart TD
    A([load_state / save_state called]) --> B{Upstash Redis configured?}
    B -- Yes --> C[Read/Write via REST API]
    C --> D{Success?}
    D -- Yes --> E([Return data])
    D -- No --> F[Log error, return default]

    B -- No --> G{Local JSON file exists?}
    G -- Yes --> H[Read from key.json]
    H --> E
    G -- No --> I([Return default value])
Loading

Project Structure

stockwatcher/
├── watcher.py          # Main bot — all logic lives here
├── requirements.txt    # Python dependencies
├── Procfile            # For Railway/Heroku deployment
├── .gitignore          # Excludes .env, cache, and state files
└── .env                # Local secrets (never commit this)

Prerequisites

  • Python 3.9+
  • A Telegram account
  • A Screener.in account (free tier works)
  • A Railway account (for deployment) — or any server with Python
  • An Upstash account (free tier, for persistent state across restarts)

Setup Guide

1. Telegram Bot Setup

Step 1: Open Telegram and search for @BotFather

Step 2: Send /newbot and follow the prompts to name your bot

Step 3: BotFather will give you a token like:

1234567890:ABCdefGHIjklMNOpqrSTUVwxyz

Save this — it's your TELEGRAM_TOKEN.

Step 4: Get your personal chat ID by messaging @userinfobot on Telegram. It will reply with your numeric chat ID (e.g. 987654321). This is your TELEGRAM_CHAT_ID — used to seed the first subscriber.


2. Screener.in Account

Step 1: Register at screener.in

Step 2: Find or create your CANSLIM screen. The default screen URL used is:

https://www.screener.in/screens/2394874/canslim/

To use your own screen, change SCREEN_URL in watcher.py.

Step 3: Note your login email and password. These become SCREENER_EMAIL and SCREENER_PASSWORD.

⚠️ The bot uses session-based scraping (not an official API). If Screener.in changes their login flow, the scraper may break.


3. Upstash Redis (Optional but Recommended)

Without Redis, state is stored in local .json files — which are wiped on every Railway redeploy. Redis makes state survive restarts.

Step 1: Go to upstash.com and create a free account

Step 2: Create a new Redis database (select the region closest to your deployment)

Step 3: From the database dashboard, copy:

  • UPSTASH_REDIS_REST_URL (looks like https://xxx.upstash.io)
  • UPSTASH_REDIS_REST_TOKEN (a long string)

4. Local Setup

# Clone the repo
git clone https://github.com/YOUR_USERNAME/stockwatcher.git
cd stockwatcher

# Create and activate a virtual environment
python -m venv venv
source venv/bin/activate        # On Windows: venv\Scripts\activate

# Install dependencies
pip install -r requirements.txt

# Create your .env file
cp .env.example .env            # or create manually

Edit .env:

TELEGRAM_TOKEN=your_bot_token_here
TELEGRAM_CHAT_ID=your_personal_chat_id
SCREENER_EMAIL=your@email.com
SCREENER_PASSWORD=yourpassword
UPSTASH_REDIS_REST_URL=https://xxx.upstash.io
UPSTASH_REDIS_REST_TOKEN=your_token_here

Run locally:

python watcher.py

You should see:

==================================================
  STOCK WATCHER - CANSLIM SCREEN (PUBLIC BOT)
==================================================
  Logging in to screener.in...
  Login successful!
  Broadcasted message to 1 subscriber(s).

5. Deploy to Railway

Railway runs the bot as a persistent background worker using the Procfile.

Step 1: Push your code to GitHub (make sure .env is in .gitignore)

Step 2: Go to railway.app → New Project → Deploy from GitHub repo

Step 3: Select your repository

Step 4: Go to Variables tab and add all environment variables:

Variable Value
TELEGRAM_TOKEN Your bot token
TELEGRAM_CHAT_ID Your Telegram numeric ID
SCREENER_EMAIL Your screener.in email
SCREENER_PASSWORD Your screener.in password
UPSTASH_REDIS_REST_URL Upstash URL
UPSTASH_REDIS_REST_TOKEN Upstash token

Step 5: Railway will auto-detect the Procfile and run python watcher.py as a worker process.

Important: Railway's free tier has usage limits. The bot runs 24/7, so check their pricing if you hit limits.


Environment Variables

Variable Required Description
TELEGRAM_TOKEN ✅ Yes Bot token from BotFather
TELEGRAM_CHAT_ID Optional Your chat ID to seed the first subscriber
SCREENER_EMAIL ✅ Yes Screener.in login email
SCREENER_PASSWORD ✅ Yes Screener.in login password
UPSTASH_REDIS_REST_URL Optional Upstash Redis URL for persistent state
UPSTASH_REDIS_REST_TOKEN Optional Upstash Redis auth token

If UPSTASH_REDIS_REST_URL or UPSTASH_REDIS_REST_TOKEN are missing, the bot falls back to local JSON files (subscribers.json, last_stocks.json, telegram_offset.json).


Usage

Subscribe to alerts:

In Telegram, message your bot:

/start

You will receive a welcome message and be added to the subscriber list. Any user who finds and messages your bot can subscribe the same way.

What happens next:

  • Every 30 minutes, the bot checks the CANSLIM screen
  • If a stock appears that wasn't there before, everyone subscribed gets an alert
  • The bot continues running indefinitely

Alert Format

On startup:

Stock Watcher is LIVE
Started: 08:30 06-Jun-2026
Checking CANSLIM screen every 30 minutes.

When new stocks are detected:

CANSLIM ALERT
3 new stock(s) entered the screen
Time: 09:00 06-Jun

→ Tata Consultancy Services
→ Infosys Ltd
→ Dixon Technologies

Check screener.in for details.

Known Limitations

  • Scraping, not API — Screener.in doesn't offer a public API. The bot scrapes HTML. If they change their page structure or add CAPTCHAs, it will break.
  • No /stop command — There is currently no way for a user to unsubscribe. You would need to add a /stop handler that removes the chat ID from the subscriber list.
  • Session expiry — The bot handles one re-login attempt per cycle. If Screener.in's session TTL is very short, you may miss cycles.
  • No deduplication guard on alerts — If Redis goes down and state is lost, the bot will re-alert on all existing stocks as if they are new.
  • Rate limits — Screener.in may throttle or block repeated requests. The 2-second delay between pages and 30-minute check interval are conservative by design.

About

bot give new stock will come

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages