Back

Copy Trading Bot: An Informative Guide

Copy Trading Bot: An Informative Guide

Introduction

Copy trading is an excellent way to simplify trading and capitalize on the expertise of experienced traders. By following successful traders, you can bypass the complexities of market analysis and strategy development. In this article, we will walk you through creating a straightforward copy trading bot to automate your trades for free. With accurate signals, this bot can help you generate significant profits.

This article will guide you through building a copy trading bot with AI integration for enhanced safety. The bot will interact with Bybit and source signals from social groups like Telegram.

Note: The bot uses an AI model for enhanced safety, though building the AI model is beyond the scope of this guide. #ADD HERE SOME suspense next guide tell them how to do it

What Do Copy Trading Bots Do?

Copy Trading Bots are automated scripts designed to execute three main functions:

  1. Capture Trading Signals: These signals can come from various sources like Discord, Reddit, or Telegram.
  2. Interpret Signals: Signals are usually sent in a format like “long BTC @ m,” which translates to: buy Bitcoin at market price. The amount to buy depends on your bot configurations and account balance.
  3. Execute Trades on Selected Exchanges: After authenticating to the exchange API, the bot sends a request payload to open a new order for the specified position.

In addition to these three main functionalities, we have integrated a new AI model that checks signals before executing them. The AI model takes the signal as an input and returns a score indicating the likelihood that the signal is a winning one. If the score is below 0.5, the bot simply skips the signal.

Now that we understand the main functionalities of our bot, let’s draft a small diagram for its main components. It’s always beneficial to have a clear design of your project before diving into the code.

Components and Architecture of a Copy Trading Bot

From the functionalities we have above, we can extract our main components as follows:

Components

  • Alert Listener: Detects signals from various sources (e.g., Telegram) and triggers the Signal Parser.
  • Signal Parser: Converts raw signals into a structured format that the bot can use.
  • AI Signal Evaluator: Evaluates signals using an AI model to determine their potential profitability.
  • Signal Executor: Executes trades on selected exchanges based on the parsed and evaluated signals.
  • Email Notification System: Sends notifications when trades are initiated.

Now, let’s start coding

  1. Set Up Environment for Python

First, install Python and the necessary libraries:

pip install pybit discord.py
  1. Set Up Bybit Class

This class will be used to interact with the Bybit API. To establish a connection, you need an API key and a secret key. Please follow Bybit documentation to obtain those keys.

import hashlib
import hmac
from pybit.unified_trading import HTTP
import requests
import json
import time

class ByBit:
    def __init__(self, api_key, api_secret):
        self.api_key = api_key
        self.api_secret = api_secret
        self.client = HTTP(
            testnet=True,
            api_key=api_key,
            api_secret=api_secret
        )

    @staticmethod
    def get_data():
        client = HTTP(testnet=True)
        instruments_info = client.get_instruments_info(category="linear")

        if instruments_info['retCode'] != 0:
            print(f"Failed to get instruments info: {instruments_info['retMsg']}")
            return

        usdt_instruments = [
            item for item in instruments_info['result']['list']
            if item['settleCoin'] == 'USDT'
        ]

        extracted_data = {}
        for instrument in usdt_instruments:
            symbol = instrument['symbol']
            price_filter = instrument['priceFilter']
            lot_size_filter = instrument['lotSizeFilter']
            leverage_filter = instrument['leverageFilter']

            extracted_data[symbol] = {
                "symbol": symbol,
                "min_price": price_filter['minPrice'],
                "max_price": price_filter['maxPrice'],
                "tick_size": price_filter['tickSize'],
                "min_order_qty": lot_size_filter['minOrderQty'],
                "max_order_qty": lot_size_filter['maxOrderQty'],
                "qty_step": lot_size_filter['qtyStep'],
                "min_leverage": leverage_filter['minLeverage'],
                "max_leverage": leverage_filter['maxLeverage']
            }

        return extracted_data

    def get_usdt_balance(self):
        response = self.client.get_wallet_balance(accountType="CONTRACT")
        if response['retCode'] == 0:
            coins = response['result']['list'][0]['coin']
            for coin in coins:
                if coin['coin'] == 'USDT':
                    return float(coin['availableToWithdraw'])
        else:
            print(f"Failed to get wallet balance: {response['retMsg']}")
            return None

    @staticmethod
    def adjust_qty(order_size, qty_step, max_order_qty):
        adjusted_order_size = max(qty_step, min(order_size - order_size % qty_step, max_order_qty))
        return adjusted_order_size if adjusted_order_size > 0 else None

    def place_order(self, symbol, side, order_type, qty, price=None, time_in_force="GoodTillCancel"):
        order = self.client.place_order(
            category="linear",
            symbol=symbol,
            side=side,
            order_type=order_type,
            qty=qty,
            price=price,
            time_in_force=time_in_force
        )
        return order

Methods Descriptions:

  • __init__: Initializes the ByBit class with the provided API key and secret, setting up a client to interact with the Bybit API.
  • get_data: Fetches market data for instruments using the Bybit API. It filters and extracts data for instruments settled in USDT, including details like price, lot size, and leverage filters.
  • get_usdt_balance: Retrieves the USDT balance from the wallet using the Bybit API. It checks the available balance that can be withdrawn.
  • adjust_qty: Adjusts the order size to be a multiple of qty_step and not exceed max_order_qty. This ensures that the order quantity conforms to the exchange’s requirements.
  • place_order: Places an order on Bybit using the specified parameters, including symbol, side, order type, quantity, price, and time in force. This method sends a request to the Bybit API to execute the trade.

Signals Parser

A signal parser converts raw trading signals into a structured format that the bot can understand and use. This is a common task, and you can likely find useful open-source code for it.

I already have a ready-to-use parser, and I’ll provide the source code later. For now, it’s important to understand that our parser will output signals in the following format:

import json
from dataclasses import dataclass, asdict
from typing import Optional

@dataclass
class ParsedSignal:
    trader_id: str
    symbol: str
    side: str
    size: int
    average_price: Optional[float]
    price_at_exit: Optional[float]
    percentage: Optional[float]
    stop_price: Optional[float]
    stop_loss: Optional[float]
    take_profits: Optional[float]
    leverage: Optional[float]
    price_paid: Optional[float]
    quantity: float = 1.0
    use_market_price: bool = False
    is_average: bool = False
    is_update:  bool = False

Now that we have our parser and executor classes ready, let’s move to building our alert listener.

First, we need to create a server and add our bot to it.

from discord.ext import commands
import discord
SERVER_ID = 1165619852111794226

CHANNEL_ID = 1268617101963169842

BOT_TOKEN = "token here!"
# start bot with token and command !
intents = discord.Intents.default()
intents.messages = True
intents.guilds = True
intents.reactions = True
intents.members = True
intents.presences = True
intents.typing = True
intents.message_content = True
bot = commands.Bot(command_prefix='!', intents=intents)
# add intents for messages

@bot.event
async def on_ready():
    print(f'Bot is ready and connected to Discord!')

@bot.command()
async def ping(ctx):
    await ctx.send('Pong!')

@bot.event
async def on_message(message):
    if message.author == bot.user:
        return
    # check if channel is correct
    if message.channel.id != CHANNEL_ID:
        return
    print(message.content)
    # here we call our parser => evaluator => executor functions

if __name__ == '__main__':
    bot.run(BOT_TOKEN)

Integration with Evaluator Model

With our listener, parser, and executor in place, the next step is to integrate the evaluator model. This model will be accessed via an API endpoint on localhost.

The API endpoint [localhost/api/v1/evaluate](http://localhost/api/v1/evaluate) accepts two parameters: coin and position side (either short or long). It returns True if the trade signal is deemed safe.

Here’s a small function that encapsulates this API call:

import requests
API_ENDPOINT = "http://localhost:8000/api/v1/eval"

def evaluate_signal(position_side, position_symbol):
    res = requests.post(API_ENDPOINT, json={
                        "position_side": position_side, "position_symbol": position_symbol})
    # check if response data . value is true
    return bool(res.json().get('data'))

on_message final version:

For simplicity, we will set our bot to open positions worth $5 only. If our balance is below $5, we will just print a message: ‘Margin is insufficient

@bot.event
async def on_message(message):
    if message.author == bot.user:
        return
    if message.channel.id != CHANNEL_ID:
        return

    # Parse the signal from the message content
    parsed_signal = parse_signal(message.content)
    if parsed_signal is None:
        return

    # Evaluate the signal using the AI model
    is_safe = evaluate_signal(parsed_signal.side, parsed_signal.symbol)
    if not is_safe:
        return

    # Ensure the bot always opens positions worth $5
    bybit = ByBit(api_key, api_secret)
    usdt_balance = bybit.get_usdt_balance()
    if usdt_balance is None or usdt_balance < 5:
        print("Insufficient balance to open a new position")
        return

    # Adjust the quantity to match $5 worth
    market_data = bybit.get_data().get(parsed_signal.symbol)
    if not market_data:
        print(f"No market data found for symbol {parsed_signal.symbol}")
        return

    qty_step = float(market_data['qty_step'])
    price = parsed_signal.price if parsed_signal.price else market_data['tick_size']
    qty = 5 / price  # Calculate quantity for $5

    adjusted_qty = bybit.adjust_qty(qty, qty_step, float(market_data['max_order_qty']))
    if adjusted_qty is None:
        print("Order quantity is too small after adjustment")
        return

    # Execute the trade
    order = bybit.place_order(
        symbol=parsed_signal.symbol,
        side=parsed_signal.side,
        order_type="Market",
        qty=adjusted_qty,
        time_in_force="GoodTillCancel"
    )

    # Check if the order was successfully placed
    if order['retCode'] != 0:
        print(f"Failed to place order: {order['retMsg']}")
        return

    print(f"Successfully placed order: {order['result']['orderId']}")

if __name__ == '__main__':
    bot.run(BOT_TOKEN)

Important Notes

  • This bot will not support stop loss or take profit functionalities.
  • It does not have any logging mechanisms. It’s a minimal working version designed to help you automate market order execution.

Recommendations for Improvement

To make this bot more reliable and robust, consider adding:

  1. Support for Limit and Stop Loss Orders: This is crucial to avoid huge loss.
  2. Position Monitoring: Implement mechanisms to monitor open positions and ensure the bot is following the correct signals.
  3. Logging: Add logging mechanisms to track the bot’s activities, which can help in debugging and monitoring performance.

Request the Complete Trading Bot Code

Subscribe to get access to the full code with all the features discussed in this guide and more.