Stop-Loss and Take-Profit Automation for Crypto Bots
The most important risk management tool for any crypto bot. How to implement stop-loss and take-profit orders properly in Python โ including trailing stops and time-based exits.
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.
Every profitable trader has one thing in common: they cut losses fast. A bot without stop-losses is a bot waiting to blow up. Here's how to implement proper risk management automation.
Why Stops Are Essential
One trade without a stop-loss can wipe out 20 profitable trades. In crypto:
- A coin can drop 40% in an hour on bad news
- Exchange outages can prevent manual exits
- Emotional decision-making at 3 AM loses money
Automated stops remove the human element from loss management.
Basic Stop-Loss Implementation
import ccxt
import time
import os
from dotenv import load_dotenv
load_dotenv()
exchange = ccxt.binance({
'apiKey': os.getenv('BINANCE_API_KEY'),
'secret': os.getenv('BINANCE_SECRET'),
'enableRateLimit': True,
})
class Position:
def __init__(self, symbol: str, entry_price: float, quantity: float,
stop_loss_pct: float = 3.0, take_profit_pct: float = 6.0):
self.symbol = symbol
self.entry_price = entry_price
self.quantity = quantity
self.stop_loss_price = entry_price * (1 - stop_loss_pct / 100)
self.take_profit_price = entry_price * (1 + take_profit_pct / 100)
self.is_open = True
def check(self, current_price: float) -> str:
"""Check if stop/target has been hit. Returns 'stop', 'target', or 'hold'."""
if not self.is_open:
return 'closed'
pnl_pct = (current_price - self.entry_price) / self.entry_price * 100
if current_price <= self.stop_loss_price:
return 'stop'
elif current_price >= self.take_profit_price:
return 'target'
return 'hold'
def close(self):
"""Market sell the position."""
qty = exchange.amount_to_precision(self.symbol, self.quantity)
order = exchange.create_market_sell_order(self.symbol, float(qty))
self.is_open = False
return order
def monitor_positions(positions: list[Position], interval: int = 15):
"""Monitor all open positions for stop/target hits."""
while any(p.is_open for p in positions):
for pos in [p for p in positions if p.is_open]:
price = float(exchange.fetch_ticker(pos.symbol)['last'])
action = pos.check(price)
pnl = (price - pos.entry_price) / pos.entry_price * 100
print(f"{pos.symbol}: ${price:,.2f} | P&L: {pnl:+.2f}% | SL: ${pos.stop_loss_price:,.0f} | TP: ${pos.take_profit_price:,.0f}")
if action == 'stop':
print(f"๐ STOP-LOSS hit for {pos.symbol} at ${price:,.2f} ({pnl:+.2f}%)")
pos.close()
elif action == 'target':
print(f"โ
TAKE-PROFIT hit for {pos.symbol} at ${price:,.2f} (+{pnl:.2f}%)")
pos.close()
time.sleep(interval)
Trailing Stop-Loss
A trailing stop follows price upward but doesn't move when price falls. It locks in profit while letting winners run:
class TrailingStopPosition:
def __init__(self, symbol: str, entry_price: float, quantity: float,
trail_pct: float = 3.0):
self.symbol = symbol
self.entry_price = entry_price
self.quantity = quantity
self.trail_pct = trail_pct
self.highest_price = entry_price
self.stop_price = entry_price * (1 - trail_pct / 100)
self.is_open = True
def update(self, current_price: float) -> str:
if current_price > self.highest_price:
self.highest_price = current_price
self.stop_price = current_price * (1 - self.trail_pct / 100)
print(f"Trailing stop moved up to ${self.stop_price:,.2f}")
if current_price <= self.stop_price:
return 'stop'
return 'hold'
# Example: Enter BTC at $60,000 with 3% trailing stop
# If BTC rises to $65,000: stop moves to $63,050
# If BTC then drops to $63,050: stop triggered (locked in +5% gain)
Time-Based Exits
Sometimes the market doesn't reach your target. Holding too long is a risk:
from datetime import datetime, timedelta
class TimedPosition(Position):
def __init__(self, *args, max_hold_hours: float = 24, **kwargs):
super().__init__(*args, **kwargs)
self.entry_time = datetime.now()
self.expiry_time = self.entry_time + timedelta(hours=max_hold_hours)
def check(self, current_price: float) -> str:
result = super().check(current_price)
if result != 'hold':
return result
if datetime.now() >= self.expiry_time:
pnl = (current_price - self.entry_price) / self.entry_price * 100
print(f"โฐ Time-based exit after {self.entry_time} | P&L: {pnl:+.2f}%")
return 'time_exit'
return 'hold'
Risk Sizing: The 2% Rule
Never risk more than 2% of total capital on a single trade:
def calculate_position_size(
total_capital: float,
entry_price: float,
stop_price: float,
risk_pct: float = 2.0
) -> dict:
"""Calculate safe position size using 2% risk rule."""
risk_amount = total_capital * (risk_pct / 100)
price_risk_pct = abs(entry_price - stop_price) / entry_price * 100
position_value = risk_amount / (price_risk_pct / 100)
quantity = position_value / entry_price
return {
"risk_amount": f"${risk_amount:.2f}",
"position_value": f"${position_value:.2f}",
"quantity": f"{quantity:.6f}",
"max_loss": f"-${risk_amount:.2f} ({risk_pct}% of capital)",
"breakeven_fee": f"{price_risk_pct:.2f}% move needed for stop"
}
result = calculate_position_size(
total_capital=10000,
entry_price=60000,
stop_price=58200, # 3% below entry
risk_pct=2.0
)
print(result)
# position_value: $6,666.67 | max_loss: -$200 | quantity: 0.000111 BTC
Putting It All Together
def run_trade(symbol: str, capital: float, entry_price: float):
"""Complete trade with position sizing, stop-loss, and monitoring."""
# Calculate stop and target (3% SL, 9% TP = 3:1 reward-risk)
stop = entry_price * 0.97
target = entry_price * 1.09
# Calculate size
sizing = calculate_position_size(capital, entry_price, stop, risk_pct=2.0)
quantity = float(sizing['quantity'])
# Enter
order = exchange.create_market_buy_order(symbol, quantity)
print(f"Entered {symbol}: {quantity} @ ${entry_price:,}")
# Create position monitor
pos = TrailingStopPosition(symbol, entry_price, quantity, trail_pct=3.0)
# Monitor (in real usage, run in a separate thread)
while pos.is_open:
price = float(exchange.fetch_ticker(symbol)['last'])
action = pos.update(price)
if action == 'stop':
pos.close()
print(f"Exited with stop")
time.sleep(30)
The key insight: stop-losses don't prevent losses โ they limit them. A 3% loss is a speed bump. A 40% loss is a disaster. Automate your exits and protect your capital.
Related Articles
How to Use the Binance API: Complete Beginner Guide (2025)
8 min read
Crypto BotsGrid Trading Bot: Complete Setup Guide for Binance
6 min read
Crypto BotsCrypto Bot Risk Management: The 10 Rules That Separate Winners From Losers
7 min read
Crypto BotsToken Sniping Bot: How to Buy New Crypto Launches in the First Block
4 min read