Crypto Bots

How to Build a Telegram Trading Group Bot That Makes Money

Build a profitable Telegram trading group with automated signals, copy trading, and subscription management. Step-by-step guide to creating a $500-5,000/month signals business with a Python Telegram bot backend.

A
AI Agents Hubยท2026-03-09ยท5 min readยท829 words

Builder of AI agents, crypto trading bots, and open-source automation tools. Sharing practical guides on how to build, deploy, and profit from AI and DeFi technology.

The Telegram Trading Group Business Model

Telegram is the primary channel for crypto trading signals. A successful signals group generates income through:

  • Subscriptions: $20-100/month per member
  • Copy trading fees: 10-20% of profits from auto-copy subscribers
  • Affiliate commissions: $50-500 per referred trader to exchanges
  • Premium analysis: One-time paid reports

A group with 200 paying subscribers at $30/month = $6,000/month recurring revenue.

Setting Up Your Telegram Bot

# 1. Create bot via @BotFather on Telegram
# /newbot โ†’ give it a name โ†’ get token

# Install dependencies
pip install python-telegram-bot stripe redis
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes
import stripe
import redis
import json
from datetime import datetime, timedelta

# Config
BOT_TOKEN = 'your-telegram-bot-token'
PREMIUM_CHANNEL_ID = -100123456789  # Your premium channel ID
STRIPE_API_KEY = 'sk_live_...'
REDIS_URL = 'redis://localhost:6379'

stripe.api_key = STRIPE_API_KEY
r = redis.from_url(REDIS_URL)

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """Welcome message and subscription options"""
    
    keyboard = [
        [InlineKeyboardButton("๐Ÿ“Š View Plans", callback_data='show_plans')],
        [InlineKeyboardButton("๐Ÿ†“ Free Signals", callback_data='free_signals')],
        [InlineKeyboardButton("๐Ÿ“ˆ Track Record", callback_data='track_record')],
    ]
    
    reply_markup = InlineKeyboardMarkup(keyboard)
    
    await update.message.reply_text(
        "๐Ÿค– Welcome to **CryptoSignalsBot**\n\n"
        "Get AI-powered crypto trading signals with:\n"
        "โ€ข 73% win rate on 200+ verified trades\n"
        "โ€ข Entry, TP, and SL for every signal\n"
        "โ€ข Real-time alerts via Telegram\n\n"
        "Choose an option below:",
        parse_mode='Markdown',
        reply_markup=reply_markup
    )

async def show_plans(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """Show subscription plans"""
    
    keyboard = [
        [InlineKeyboardButton("Basic โ€” $29/mo", callback_data='subscribe_basic')],
        [InlineKeyboardButton("Pro โ€” $79/mo", callback_data='subscribe_pro')],
        [InlineKeyboardButton("Elite โ€” $199/mo", callback_data='subscribe_elite')],
        [InlineKeyboardButton("โ† Back", callback_data='back_to_menu')],
    ]
    
    reply_markup = InlineKeyboardMarkup(keyboard)
    
    await update.callback_query.edit_message_text(
        "๐Ÿ’Ž **Subscription Plans**\n\n"
        "**Basic** โ€” $29/mo\nโ€ข Daily signals (3-5/day)\nโ€ข BTC/ETH only\n\n"
        "**Pro** โ€” $79/mo\nโ€ข All signals (5-10/day)\nโ€ข All major coins\nโ€ข Alert notifications\n\n"
        "**Elite** โ€” $199/mo\nโ€ข All Pro features\nโ€ข Copy trading\nโ€ข 1-on-1 analysis\nโ€ข Private group access",
        parse_mode='Markdown',
        reply_markup=reply_markup
    )

async def handle_subscription(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """Create Stripe checkout for subscription"""
    
    query = update.callback_query
    plan = query.data.replace('subscribe_', '')
    
    prices = {
        'basic': 'price_1234basic',  # Your Stripe price IDs
        'pro': 'price_1234pro',
        'elite': 'price_1234elite',
    }
    
    plan_names = {'basic': 'Basic', 'pro': 'Pro', 'elite': 'Elite'}
    
    telegram_id = query.from_user.id
    
    # Create Stripe checkout session
    session = stripe.checkout.Session.create(
        payment_method_types=['card'],
        line_items=[{
            'price': prices[plan],
            'quantity': 1,
        }],
        mode='subscription',
        success_url=f'https://your-site.com/success?session_id={{CHECKOUT_SESSION_ID}}&telegram_id={telegram_id}',
        cancel_url='https://your-site.com/cancel',
        metadata={
            'telegram_id': str(telegram_id),
            'plan': plan,
        }
    )
    
    keyboard = [[InlineKeyboardButton("๐Ÿ’ณ Subscribe Now", url=session.url)]]
    
    await query.edit_message_text(
        f"Click below to subscribe to **{plan_names[plan]}**:\n\n"
        f"You'll be redirected to secure Stripe checkout.\n"
        f"After payment, you'll automatically get channel access.",
        parse_mode='Markdown',
        reply_markup=InlineKeyboardMarkup(keyboard)
    )

Auto-Adding Users After Payment

from flask import Flask, request
import hmac
import hashlib

webhook_app = Flask(__name__)

async def add_user_to_premium_channel(telegram_id: int, plan: str, duration_days: int = 30):
    """Add user to premium Telegram channel"""
    from telegram import Bot
    bot = Bot(token=BOT_TOKEN)
    
    try:
        # Create invite link for this specific user
        invite = await bot.create_chat_invite_link(
            PREMIUM_CHANNEL_ID,
            member_limit=1,
            expire_date=datetime.now() + timedelta(days=duration_days),
            name=f"User {telegram_id} {plan}"
        )
        
        # Send invite link to user
        await bot.send_message(
            telegram_id,
            f"โœ… **Payment successful!**\n\n"
            f"You're subscribed to {plan.title()} plan.\n\n"
            f"Join your premium channel here:\n{invite.invite_link}\n\n"
            f"This link expires in 24 hours and is single-use.",
            parse_mode='Markdown'
        )
        
        # Save subscription in Redis
        r.setex(
            f"sub:{telegram_id}",
            duration_days * 86400,  # TTL in seconds
            json.dumps({'plan': plan, 'joined': datetime.now().isoformat()})
        )
        
        print(f"โœ… Added user {telegram_id} to premium channel ({plan})")
        
    except Exception as e:
        print(f"Error adding user: {e}")

@webhook_app.route('/stripe-webhook', methods=['POST'])
def stripe_webhook():
    """Handle Stripe payment webhooks"""
    
    payload = request.data
    sig_header = request.headers.get('Stripe-Signature')
    
    # Verify webhook signature
    try:
        event = stripe.Webhook.construct_event(
            payload, sig_header, STRIPE_WEBHOOK_SECRET
        )
    except ValueError:
        return 'Invalid payload', 400
    
    if event['type'] == 'checkout.session.completed':
        session = event['data']['object']
        telegram_id = int(session['metadata']['telegram_id'])
        plan = session['metadata']['plan']
        
        import asyncio
        asyncio.run(add_user_to_premium_channel(telegram_id, plan))
    
    elif event['type'] == 'customer.subscription.deleted':
        # Handle cancellation
        customer_id = event['data']['object']['customer']
        # Remove user from channel
        pass
    
    return 'OK', 200

Automated Signal Posting

async def post_signal(
    coin: str,
    direction: str,  # 'LONG' or 'SHORT'
    entry: float,
    targets: list[float],
    stop_loss: float,
    confidence: str = 'HIGH',
    analysis: str = ''
) -> None:
    """Post a trading signal to the premium channel"""
    
    from telegram import Bot
    bot = Bot(token=BOT_TOKEN)
    
    emoji = "๐Ÿ“ˆ" if direction == 'LONG' else "๐Ÿ“‰"
    
    message = f"{emoji} **{coin} {direction}**\n\n"
    message += f"**Entry:** ${entry:,.2f}\n"
    message += f"**Stop Loss:** ${stop_loss:,.2f}\n\n"
    
    for i, target in enumerate(targets, 1):
        pnl = abs((target - entry) / entry * 100)
        message += f"**TP{i}:** ${target:,.2f} (+{pnl:.1f}%)\n"
    
    message += f"\n**Confidence:** {confidence}\n"
    
    if analysis:
        message += f"\n๐Ÿ“Š **Analysis:** {analysis}\n"
    
    rr = abs((targets[0] - entry) / (entry - stop_loss))
    message += f"\nโšก R/R: 1:{rr:.1f}"
    
    await bot.send_message(
        PREMIUM_CHANNEL_ID,
        message,
        parse_mode='Markdown'
    )

# Example signal
await post_signal(
    coin="BTC",
    direction="LONG",
    entry=64500,
    targets=[67000, 70000, 75000],
    stop_loss=62000,
    confidence="HIGH",
    analysis="Break above weekly resistance with volume confirmation. RSI reset from oversold."
)

Building Credibility (The Key to Success)

A signals business lives and dies by track record. Before monetizing:

  1. Post 30+ free signals publicly to establish credibility
  2. Track everything: Entry, exit, date, P&L
  3. Post losses too โ€” selective reporting destroys trust instantly
  4. Use a third-party tracker like Zignaly Signal History for verifiable results

The technical bot is easy to build. The hard part is generating consistently profitable signals and maintaining transparent reporting. Focus 80% of your energy on signal quality, 20% on the bot infrastructure.

Related Articles