A trader runs a signal channel in Telegram, but you can't manually open trades fast enough? A copy trading bot solves this: it automatically tracks the trader's positions and replicates them on your account. Unlike off-the-shelf solutions, our development allows flexible configuration of sources, risk management, and scaling algorithms. Execution latency is critical: a 200 ms difference can eat 5% of profit. Our bots use low-level APIs and WebSocket for minimal latency. We also implement logging and monitoring via Tenderly for on-chain trades.
One of our clients, managing a $2M portfolio, was suffering 0.3% slippage when copying via Telegram. We migrated him to direct exchange API and reduced latency from 400 ms to 80 ms, saving about $600 per month in slippage.
Why latency is critical
Latency between signal and execution directly impacts profitability. The longer the signal processing, the more likely the price moves against you. In high-frequency copying, even 100 ms can mean the difference between profit and loss. We use WebSocket and asynchronous calls to speed things up.
Which signal sources does the bot use?
The choice of source determines system latency and reliability. Let's compare three main options:
| Source | Typical latency | Reliability | Integration complexity |
|---|---|---|---|
| Exchange API (sub-account) | 50–200 ms | High | Medium |
| Telegram webhook | 200–500 ms | Medium | Low |
| On-chain monitoring | 1–3 seconds | Low (depends on network) | High |
Via Exchange API
If the source trades on the same exchange, polling its positions via API is the most reliable option:
class ExchangeCopyTrader:
def __init__(self, source_api_key: str, follower_api_key: str, exchange: str):
self.source_client = ExchangeClient(source_api_key)
self.follower_client = ExchangeClient(follower_api_key)
self.tracked_positions: dict = {}
async def sync_positions(self):
"""Sync positions every N seconds"""
source_positions = await self.source_client.get_open_positions()
follower_positions = await self.follower_client.get_open_positions()
source_map = {p.symbol: p for p in source_positions}
follower_map = {p.symbol: p for p in follower_positions}
# New positions on source — open on follower
for symbol, pos in source_map.items():
if symbol not in follower_map:
await self.open_copied_position(pos)
# Positions closed on source — close on follower
for symbol in follower_map:
if symbol not in source_map:
await self.close_copied_position(symbol)
# Position size changed — adjust
for symbol in source_map:
if symbol in follower_map:
source_size = source_map[symbol].size
follower_size = follower_map[symbol].size
scaled_size = source_size * self.config.scale_factor
if abs(follower_size - scaled_size) / scaled_size > 0.05:
await self.adjust_position(symbol, scaled_size)
Via Telegram webhook
Many traders publish signals in Telegram channels. The bot parses messages:
class TelegramSignalParser:
# Pattern to parse: "BUY BTCUSDT @ 50000, SL: 48000, TP: 55000"
SIGNAL_PATTERN = r'(BUY|SELL)\s+(\w+)\s+@\s+([\d.]+)(?:.*SL:\s*([\d.]+))?(?:.*TP:\s*([\d.]+))?'
def parse_message(self, text: str) -> TradeSignal | None:
import re
match = re.search(self.SIGNAL_PATTERN, text, re.IGNORECASE)
if not match:
return None
return TradeSignal(
action=match.group(1).upper(),
symbol=match.group(2).upper(),
entry_price=float(match.group(3)),
stop_loss=float(match.group(4)) if match.group(4) else None,
take_profit=float(match.group(5)) if match.group(5) else None,
source='telegram'
)
On-chain wallet monitoring
For DeFi: monitoring on-chain transactions of a known wallet (whale tracking). We listen to Swap events via WebSocket and filter transactions with amount > $1000. This allows copying large wallet trades on Uniswap. Implementation uses web3.py and log subscription.
How is the copied position size calculated?
We implement three scaling modes:
| Mode | Description | When to use |
|---|---|---|
| Proportional | Copies the same % of capital as the trader | When capital is comparable |
| Fixed | Copies a specified volume (max 10% of follower balance) | When a per-trade limit is needed |
| Fixed risk | Limits risk per trade as % of balance | For aggressive trading |
def calculate_copy_size(
source_trade: Trade,
source_balance: float,
follower_balance: float,
mode: str = 'proportional',
multiplier: float = 1.0
) -> float:
if mode == 'proportional':
# Copy same % of capital
source_percent = source_trade.size_usd / source_balance
return follower_balance * source_percent * multiplier
elif mode == 'fixed':
return min(source_trade.size_usd * multiplier, follower_balance * 0.1)
elif mode == 'fixed_risk':
# Fixed risk per trade (% of balance)
if source_trade.stop_loss:
risk_percent = abs(source_trade.entry - source_trade.stop_loss) / source_trade.entry
max_loss = follower_balance * (self.config.risk_per_trade / 100)
return max_loss / risk_percent
return follower_balance * 0.02 # default 2%
How to minimize latency?
Latency between signal and execution is critical: while your bot processes the signal, the price moves. Copying via API is 2–3 times faster than Telegram signals.
class LatencyMonitor:
def __init__(self):
self.latencies = []
async def execute_with_tracking(self, signal: TradeSignal) -> Execution:
t0 = time.perf_counter()
# Execute order
order = await self.exchange.place_market_order(
symbol=signal.symbol,
side=signal.action.lower(),
amount=self.calculate_size(signal)
)
t1 = time.perf_counter()
latency_ms = (t1 - t0) * 1000
self.latencies.append(latency_ms)
logger.info(f"Copy latency: {latency_ms:.1f}ms, fill: {order.fill_price}")
# Alert if too slow
if latency_ms > 500:
await self.alert(f"High latency: {latency_ms:.0f}ms for {signal.symbol}")
return order
Typical latencies for different sources:
- Exchange sub-account API → ~50–200ms
- Telegram webhook → ~200–500ms
- On-chain monitoring → ~1000–3000ms (depends on network)
What does risk management include for a copy bot?
class CopyBotRiskManager:
def can_copy(self, signal: TradeSignal, account_state: AccountState) -> tuple[bool, str]:
# Overall drawdown
if account_state.drawdown_percent > self.config.max_drawdown:
return False, "max_drawdown_exceeded"
# Number of simultaneous positions
if len(account_state.open_positions) >= self.config.max_positions:
return False, "max_positions_reached"
# Already have a position in this symbol
if signal.symbol in account_state.open_positions:
return False, "symbol_already_open"
# Minimum balance to open
required = self.calculate_required_margin(signal)
if account_state.free_balance < required * 1.1: # 10% buffer
return False, "insufficient_balance"
return True, "ok"
Turnkey bot development process
- Requirements analysis — define signal sources, copying modes, risk parameters.
- Architecture design — choose tech stack (Python/Go, exchange APIs, message broker).
- Implementation — write bot core, parsers, risk manager.
- Testing — on historical data and real signals (backtest + paper trade).
- Deployment — deploy on VPS with monitoring (Grafana, alerts).
- Training and documentation — hand over source code, manual, set up support.
A basic version with one signal source takes 5–10 business days. A full system with multiple sources and advanced risk management takes 15–25 days.
Ready to discuss your project? Contact us for a free consultation. Get a demo version of the bot on your signals.
What's included
- Configuration of signal sources (API, Telegram, on-chain).
- Implementation of copying algorithms (proportional, fixed, fixed risk).
- Integration of risk management (drawdown, limits, stop-losses).
- Testing on historical data and paper trade.
- Server deployment with monitoring.
- Documentation and training.
- Support for one month after launch.
We guarantee transparency at every step: all source code stays with you, no hidden fees. Order a custom bot development — we'll propose a solution tailored to your strategy.







