How to Build an AI Trading Bot: A Complete Developer's Guide (2026)
Author: Usman Asim

In the past few years, we've witnessed a huge proliferation of AI trading tools.
Around 58% of retail investors now use some form of AI to assist them in building their portfolios, venture capital has poured $213 million into Web3 AI trading tech, and some autonomous AI agents are already processing over $400 million in trading volume in just one month, getting increasingly sophisticated with market analysis, execution speed, and coordination with other agents.
But beneath the hype lies a more nuanced reality, one where success depends not just on sophisticated algorithms, but on reliable infrastructure, sound risk management, and realistic expectations about what AI can and cannot do.
This comprehensive guide will walk you through everything you need to know to build your own AI trading bot. We'll explore the current landscape of AI trading, including emerging autonomous agent economies, survey the tools and platforms available, break down different bot strategies, and provide you with a tutorial to get started on building your own trading bot using Alchemy's blockchain infrastructure.
What Is an AI Trading Bot?
An AI trading bot is software that uses machine learning algorithms to analyze market data, identify trading opportunities, and execute buy or sell orders automatically. Unlike traditional rule-based bots that follow static "if-then" logic, AI-powered bots can adapt to changing market conditions, learn from historical patterns, and make probabilistic decisions based on vast datasets. They operate 24/7, removing emotional bias from trading decisions and reacting to market movements faster than any human could.
The AI trading bot landscape in 2025 is a study in contrasts. Adoption of AI-assisted trading tools has grown significantly, with more retail investors incorporating automation into their strategies than ever before. However, the actual success rate tells a more sobering story: only 10-30% of bot users achieve consistent profitability.
This gap between adoption and success isn't surprising as crypto markets are notoriously volatile, predictive models require constant refinement, and many newcomers underestimate the importance of risk management. The bots that succeed aren't necessarily the ones with the most sophisticated AI, they're the ones built on solid fundamentals: reliable data, clear strategies, and disciplined execution.
The lesson? AI trading bots are powerful tools, but they're not magic & success depends on understanding both their capabilities and their limitations.
The Role of Blockchain Infrastructure
AI trading bots are powerful tools, but success depends on understanding both their capabilities and their limitations. What often gets overlooked, though, is that model sophistication is only part of the equation. Most bots don't fail because their AI made bad predictions, they fail because the infrastructure underneath couldn't keep up. The data was stale. The API went down during a volatile moment. A competitor with faster pipes got to the trade first. These aren't edge cases; they're the everyday realities of algorithmic trading.
There are three key infrastructure requirements that separate bots that consistently profit from those that blow up:
Data accuracy: AI models are only as good as the data they're trained on and react to. Stale token prices, outdated liquidity pool states, or missed whale movements translate directly into poor trading decisions. When your bot acts on bad data, it loses money—there's no algorithmic cleverness that compensates for fundamentally flawed inputs.
Latency: In algorithmic trading, speed determines who profits. When your bot identifies an arbitrage opportunity, dozens of others likely see the same signal. The bot with the fastest data pipeline executes first and captures the spread; everyone else gets worse fills or misses the trade entirely. Low-latency infrastructure is a competitive requirement, not a performance optimization.
Reliability: Infrastructure downtime during volatile markets can be catastrophic. A few minutes offline might mean missed stop-losses during a crash, liquidated positions you couldn't defend, or opportunities that passed before your bot reconnected. For trading systems managing real capital, uptime isn't a service-level metric—it's a survival requirement.
This is where a data provider like Alchemy becomes essential. Alchemy provides reliable, real-time data access that AI trading bots require to function effectively—token prices, transaction volumes, whale movements, liquidity pool states, and more across multiple chains. Alchemy's enhanced APIs and infrastructure handle these requirements at scale, supporting over 100 blockchains with the low-latency performance that competitive trading demands.
Building Safe and Observable Trading Bots
Even with solid infrastructure, AI trading bots can behave unpredictably. Markets move in ways models don't anticipate, edge cases trigger unexpected behavior, and compounding errors can turn small mistakes into catastrophic losses. Building safety mechanisms into your bot's architecture isn't optional: it's how you survive long enough to iterate and improve.
Build observable systems: Every trade should be traceable. What data informed the decision? Why did the bot choose that particular action? When something goes wrong (and it will), you need to diagnose whether the issue was bad data, flawed logic, or an edge case your model didn't anticipate.
Add circuit breakers: Set maximum loss thresholds that automatically pause trading. Implement rate limiters to prevent runaway execution. Build in cooldown periods after significant losses. These mechanisms prevent a malfunctioning bot from draining your account before you notice something is wrong.
Keep humans in the loop: Fully autonomous sounds impressive, but the most resilient systems keep humans involved for high-stakes decisions. Consider requiring manual approval for trades above certain thresholds, or building alert systems that notify you when the bot's behavior deviates from expected patterns.
The bots that succeed long-term aren't just the ones with the best models, they're the ones built with enough observability and safety rails to catch problems early and recover gracefully.
Tools and Technologies: Your AI Trading Bot Stack
With the infrastructure layer of AI trading bots covered, data accuracy, latency, and reliability, the next step is assembling the actual toolkit. Here's what goes into a production-ready AI trading bot stack:
Core Programming Stack
Python remains the dominant language for AI trading bots thanks to its extensive machine learning ecosystem and simplicity. Your core Python libraries should include:
Pandas: For data manipulation and time series analysis
NumPy: For numerical computations on price and volume data
scikit-learn: For traditional machine learning models (regression, classification, clustering)
PyTorch or TensorFlow: For deep learning models like LSTMs or transformers for sequence prediction
Together, these libraries give you the full spectrum: from a simple moving average crossover bot you can build in an afternoon to a sophisticated multi-model bot that fuses technical analysis, sentiment scoring, and on-chain metrics. This is the beauty of Python's ecosystem: you can start simple and add complexity as you need it, no need to architect for deep learning on day one if a linear regression gets the job done.
A note: If you're building high-frequency trading bots where microseconds matter, C++ offers raw speed and low-level control. Rust has also gained traction in crypto for its performance and memory safety, particularly if you're building on-chain components or custom DEX integrations where security is paramount. Go is another solid choice for concurrent systems that need to monitor multiple markets simultaneously.
With that said, for most crypto trading strategies, even sophisticated ones, Python's performance is more than adequate, and the development velocity you gain far outweighs marginal speed improvements for most retail traders. Start with Python; then feel free to migrate critical components to faster languages if profiling shows you actually need it.
Blockchain Infra: The Alchemy Layer
This is where Alchemy becomes essential. Alchemy provides the infrastructure layer that powers reliable AI trading bots at scale. Key tools include:
Real-time blockchain data: Query token prices, balances, transaction histories, and NFT metadata across 50+ chains. Try it for yourself with our sandbox.
WebSocket connections: Subscribe to real-time events like pending transactions, new blocks, or smart contract state changes.
Enhanced APIs: Access gas price predictions, token balances with metadata, and historical transaction data.
Smart Wallets: Execute trades programmatically with built-in security features like spending limits.
For crypto trading bots, you'll also want:
Web3.py: Your interface for Ethereum interactions: sending transactions, calling smart contracts, encoding data. Essential if you're trading on DEXs or interacting with DeFi protocols directly.
CCXT: A unified API wrapper that lets you trade on 100+ centralized exchanges (Binance, Coinbase, Kraken, etc.) using consistent syntax. Instead of learning each exchange's API quirks, CCXT normalizes everything.
Alchemy's MCP (Modern Context Protocol) server: This is where blockchain meets AI: our MCP servers enables AI agents to query on-chain data using natural language. Instead of writing complex queries, your AI can ask "what's the current price of ETH?" and get structured data back.
AI Enhancement Tools
We have seen the next evolution of trading bots not just analyzing data, but using AI to make increasingly sophisticated decisions. Two major methods emerged in the past year that different developers have been using to refine their processes:
Large Language Model APIs (Claude, GPT-4, etc.) unlock capabilities beyond traditional ML:
Sentiment analysis: Process thousands of tweets, Reddit posts, or news articles to gauge market mood
Strategy generation: Describe a trading idea in plain English and have the AI help formalize it into code
Anomaly detection: Use LLMs to spot unusual patterns in market data that might signal opportunities
In 2025, we were introduced to new Agent frameworks that take this further by building autonomous systems:
ElizaOS (from ai16z): The framework behind decentralized AI funds, allowing you to build agents that don't just suggest trades: they execute them. They canmanage portfolios, and even coordinate with other agents.
Virtuals GAME SDK: Create agents that participate in larger agent economies: earning fees, providing services to other agents, or collaborating on complex strategies. You can think of it as building a bot that can "play well with others."
LangChain: The Swiss Army knife for orchestrating complex AI workflows, allowing you to mold together multiple AI calls, combine data from different sources, and build decision trees that would be tedious to hardcode. Particularly useful when your strategy requires multiple steps of reasoning.
These tools shift you from a "bot that follows rules" to an "agent that adapts and learns." The learning curve for development is steeper, but the potential payout is significantly higher.
Essential Supporting Tools
APIs for market data: Services like CoinGecko, CoinMarketCap, or exchange-specific APIs (Binance, Coinbase, Kraken) provide pricing data, trading volumes, market cap rankings, and historical OHLCV (open, high, low, close, volume) data. While Alchemy handles onchain data, these APIs fill the gap for centralized exchange prices and broader market context that your models need for informed decision-making.
Backtesting frameworks: Before risking real capital, you need to validate your strategy against historical data. There are frameworks like Backtrader and Zipline that let you simulate how your bot would have performed during past market conditions, during times of bull runs, crashes, and sideways chop. Good backtesting infrastructure helps you identify strategy weaknesses, optimize parameters, and build confidence before going live.
Version control: Git isn't optional for serious trading systems. As your strategies evolve. tweaking parameters, adding new signals, fixing bugs, you need to track every change, roll back failed experiments, and maintain clear history of what code was running when specific trades executed. This becomes critical for debugging and auditing your bot's behavior over time.
Types of AI Trading Bots
AI trading bots span a wide range of strategies and complexity levels, from straightforward automation to experimental multi-agent systems. Here are the most common categories you'll encounter:
Technical and Sentiment Analysis Bots: Technical bots analyze price charts, moving averages, and indicators to identify trading opportunities—AI enhancements let them dynamically weight signals and recognize complex patterns that static rules would miss. Sentiment bots parse Twitter/X posts, Reddit discussions, news articles, and onchain signals (whale movements, exchange inflows, gas spikes) to predict short-term price movements based on market mood. Particularly popular for altcoins and meme tokens where hype cycles drive volatility.
Arbitrage and Market-Making Bots: Arbitrage bots exploit price differences across exchanges or liquidity pools—buying ETH at $2,000 on one venue and selling at $2,010 on another. AI helps by predicting discrepancies before they occur and optimizing multi-hop routes across DEXs. Market-making bots provide liquidity by simultaneously offering to buy and sell, profiting from the spread. In DeFi, this means depositing into Uniswap or Curve pools and using AI to manage impermanent loss and adjust spreads based on volatility.
Trend-Following and Portfolio Management Bots: Momentum bots identify assets gaining strength and ride trends until signals reverse, models like LSTMs (Long Short-Term Memory networks) help predict whether a trend will continue or break down. Portfolio bots automatically rebalance holdings based on AI predictions, shifting allocations toward assets with stronger expected returns while managing overall risk exposure.
Autonomous AI Agent Funds: At the experimental edge, projects like ElizaOS manage DAO-governed funds where AI agents coordinate trading strategies, share signals, and optimize collective performance, similar to aixbt. These multi-agent systems blur the line between trading bots and autonomous financial entities, still early, but a glimpse of where AI-driven finance is heading.
Building Your First AI Trading Bot: A Step-by-Step Tutorial
In this section, we'll try and create our own sentiment-enhanced trend prediction bot for Ethereum that combines on chain data with machine learning to make trading decisions.
What we're building:
A bot that fetches real-time ETH price and blockchain data via Alchemy
Feature engineering that combines price indicators with on-chain sentiment signals
A machine learning model that predicts short-term price movements
Trading logic with confidence thresholds and risk management
Backtesting capabilities to validate the strategy before going live
This is a primitive example: think of it as a foundation you can build on, not a production-ready system. The goal is to understand the core concepts and workflow, then extend it based on your specific strategy.
Prerequisites
Before we start coding, make sure you have:
Python 3.8 or higher installed on your machine
An Alchemy account with an API key (sign up free here)
Basic Python knowledge: You should be comfortable with functions, loops, and data structures
Basic ML understanding: Familiarity with concepts like training/testing splits and model evaluation
First, we need to install the required packages:
pip install alchemy-sdk pandas numpy scikit-learn requests web3 python-dotenvWhat each of these packages does:
alchemy-sdk: Connects to Alchemy's blockchain APIspandas: Handles data manipulation and time seriesnumpy: Powers numerical computationsscikit-learn: Provides machine learning algorithmsrequests: Makes HTTP requests to APIsweb3: Interacts with Ethereum blockchainpython-dotenv: Manages environment variables securely
Step 1: Set Up Alchemy and Fetch Historical Blockchain Data
First, let's establish our connection to the blockchain and gather historical data that we'll use to train our model.
Create a .env file in your project directory to store your API key securely:
ALCHEMY_API_KEY=your_api_key_hereNow let's create a file called blockchain_data_fetcher.py and write the code to fetch blockchain data:
import os
from dotenv import load_dotenv
from alchemy import Alchemy, Network
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
import time
# Load environment variables from .env file
load_dotenv()
# Initialize Alchemy SDK
alchemy = Alchemy(
api_key=os.getenv('ALCHEMY_API_KEY'),
network=Network.ETH_MAINNET
)
def fetch_historical_data(hours=168): # Default: last week (168 hours)
"""
Fetch historical blockchain data that we'll use as features.
We're collecting:
- Block numbers and timestamps (for temporal ordering)
- Gas used per block (proxy for network activity)
- Transaction counts (proxy for market interest)
These on-chain metrics can signal market conditions before they
appear in price data.
"""
current_block = alchemy.core.get_block_number()
# Ethereum produces ~1 block every 12 seconds# So roughly 300 blocks per hour
blocks_per_hour = 300
blocks_to_fetch = hours * blocks_per_hour
# We'll sample every N blocks to avoid excessive API calls# Sampling hourly (every 300 blocks) gives us manageable data
data = []
for i in range(current_block - blocks_to_fetch, current_block, blocks_per_hour):
try:
block = alchemy.core.get_block(i)
data.append({
'block_number': i,
'timestamp': block.timestamp,
'gas_used': block.gas_used,
'transaction_count': len(block.transactions)
})
except Exception as e:
print(f"Error fetching block {i}: {e}")
continue
return pd.DataFrame(data)
# Fetch a week of data
print("Fetching historical blockchain data...")
df = fetch_historical_data(hours=168)
print(f"Retrieved {len(df)} data points")
print(df.head())This script initializes the Alchemy with your API key and connects to Ethereum mainnet. It then calculates how many blocks to fetch based on your desired time range, Ethereum's roughly 12-second block time means about 300 blocks per hour. To keep the dataset manageable and avoid excessive API calls, we sample one block per hour rather than fetching every block. For each sampled block, we extract gas usage and transaction count as proxies for network activity, then store everything in a pandas DataFrame for easy manipulation in later steps.
These onchain metrics often provide early signals of market activity before they show up in price data. When gas usage spikes, it typically indicates increased trading activity, DeFi interactions, or major onchain events, all of which can precede price volatility. Similarly, rising transaction counts can signal growing interest in ETH or heightened DeFi activity. By incorporating these blockchain-native signals into our model alongside traditional price data, we're giving our bot information that purely price-based strategies would miss.
Step 2: Fetch ETH Price Data Using Alchemy
Blockchain data alone isn't enough: we need actual price information to train our model. Lets create a file called fetch_eth_price.py and integrate Alchemy's Prices API, which provides real-time prices across multiple chains with low latency.
Why Alchemy's Prices API?
Unified provider: Same API key, same rate limits, consistent billing
Exchange aggregation: Prices reflect actual market conditions across multiple exchanges
Low latency: Optimized for trading applications (sub-100ms typical response)
Multi-chain ready: Same API structure works for any token on any supported chain
import requests
from datetime import datetime, timedelta
def fetch_eth_prices_alchemy(hours=168):
"""
Fetch historical ETH prices using Alchemy's Token Prices API.
This gives us reliable, exchange-aggregated pricing that reflects
actual market conditions across major venues.
"""
# Alchemy's prices endpoint
url = f"https://eth-mainnet.g.alchemy.com/prices/v1/{os.getenv('ALCHEMY_API_KEY')}/tokens/by-symbol"
# Calculate our time window
end_time = datetime.now()
start_time = end_time - timedelta(hours=hours)
params = {
'symbols': 'ETH',
'startTime': int(start_time.timestamp()),
'endTime': int(end_time.timestamp()),
'interval': '1h' # Hourly price points
}
headers = {
'Accept': 'application/json'
}
response = requests.get(url, params=params, headers=headers)
if response.status_code == 200:
data = response.json()
prices = data['data'][0]['prices']
# Convert to DataFrame format
price_data = []
for price_point in prices:
price_data.append({
'timestamp': datetime.fromtimestamp(price_point['timestamp']),
'price': price_point['value']
})
return pd.DataFrame(price_data)
else:
raise Exception(f"Failed to fetch prices: {response.status_code} - {response.text}")
# Fetch prices
print("Fetching ETH price data from Alchemy...")
price_df = fetch_eth_prices_alchemy(hours=168)
# Ensure timestamps are datetime objects for merging
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='s')
# Merge blockchain data with price data# merge_asof handles slight timestamp mismatches by finding nearest match
df = pd.merge_asof(
df.sort_values('timestamp'),
price_df.sort_values('timestamp'),
on='timestamp',
direction='nearest'
)
print(f"Merged data shape: {df.shape}")
print(df.head())This script fetches historical ETH prices from Alchemy's Token Prices API over the same time window as our blockchain data (168 hours by default). We define a start and end time, request hourly price points, and parse the response into a pandas DataFrame with timestamps and price values.
The key step at the end is merging this price data with our blockchain data from Step 1. We use merge_asof instead of a standard merge because timestamps from block data and price data won't align perfectly, merge_asof finds the nearest matching timestamp for each row, ensuring we don't lose data due to minor timing differences. After this step, we have a unified DataFrame containing both onchain metrics (gas usage, transaction counts) and price data, ready for feature engineering in the next step.
Step 3: Engineer Features for Machine Learning
Now that we got our raw data, we need need to transform it into features that capture patterns the model can learn from. This is where domain knowledge meets data science.
def engineer_features(df):
"""
Transform raw data into predictive features.
We're creating two types of features:
1. Price-based technical indicators (moving averages, volatility)
2. On-chain sentiment proxies (gas and transaction trends)
The goal is to give the model multiple perspectives on market conditions.
"""
# ============================================# PRICE-BASED FEATURES# ============================================
# Percentage change from previous hour# Captures momentum - is price accelerating up or down?
df['price_change'] = df['price'].pct_change()
# Moving averages - smooth out noise, identify trends
df['price_ma_12'] = df['price'].rolling(window=12).mean() # 12-hour
df['price_ma_24'] = df['price'].rolling(window=24).mean() # 24-hour
# When short-term MA crosses above long-term MA = bullish signal# When it crosses below = bearish signal
# Volatility - standard deviation of recent price changes# High volatility = risky/unstable market
df['volatility'] = df['price_change'].rolling(window=12).std()
# ============================================# ON-CHAIN SENTIMENT FEATURES# ============================================
# Gas usage trend - are people paying more to transact?# Spikes often precede price movements
df['gas_trend'] = df['gas_used'].pct_change()
# Transaction count trend - is activity increasing?# More transactions = more interest = potential price catalyst
df['tx_trend'] = df['transaction_count'].pct_change()
# ============================================# MOMENTUM INDICATORS# ============================================
# Price change over last 6 hours# Positive momentum = upward trajectory
df['momentum'] = df['price'] - df['price'].shift(6)
# ============================================# TARGET VARIABLE# ============================================
# What we're trying to predict: will price go up in the next hour?# 1 = yes (buy signal), 0 = no (sell/hold signal)
df['target'] = (df['price'].shift(-1) > df['price']).astype(int)
# Drop rows with NaN values (from rolling windows and shifts)
df = df.dropna()
return df
df = engineer_features(df)
print(f"\nEngineered features: {df.columns.tolist()}")
print(f"Data shape: {df.shape}")
print(f"\nSample of engineered data:")
print(df[['price', 'price_change', 'volatility', 'momentum', 'target']].head())This function transforms our raw blockchain and price data into features that a machine learning model can use to make predictions. Each feature captures a different signal about market conditions, some derived from price movements, others from onchain activity. Here's what each feature represents and why it matters for predicting price direction:
Price change (
pct_change): Converts absolute price movements to percentages. Going from $2,000 to $2,020 is a 1% move—more meaningful and comparable than simply "$20 up." Percentages normalize changes across different price levels.Moving averages (12-hour and 24-hour): These smooth out short-term price noise to reveal underlying trends. When the short-term MA crosses above the long-term MA, it's traditionally interpreted as a bullish signal; crossing below suggests bearish momentum. The model can learn these crossover patterns.
Volatility: Measured as the standard deviation of recent price changes. High volatility indicates an unstable market with larger price swings—higher risk but also potentially higher reward. Some strategies avoid volatile periods while others specifically target them.
Gas and transaction trends: Onchain metrics that can lead price movements. If gas usage suddenly spikes, something significant might be happening (major NFT drop, DeFi exploit, whale activity) that could affect ETH price before it shows up in trading data.
Momentum: A simple but effective measure of whether price is trending upward over the last 6 hours. Positive momentum suggests continued upward movement; negative momentum suggests decline. Momentum traders use these signals to ride existing trends.
Target variable: This is what we're training the model to predict: will the price be higher in the next hour than it is now? A value of 1 means yes (potential buy signal), 0 means no (potential sell or hold signal). This binary classification approach simplifies the prediction problem.
Step 4: Train a Machine Learning Model
Now we train a model to learn patterns from our features. We're using Random Forest: an ensemble method that's robust to noisy data and doesn't require extensive hyperparameter tuning.
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import accuracy_score, classification_report
# Select the features we engineered
feature_columns = [
'price_change', 'price_ma_12', 'price_ma_24', 'volatility',
'gas_trend', 'tx_trend', 'momentum'
]
X = df[feature_columns]
y = df['target']
# Split data: 80% for training, 20% for testing# CRITICAL: shuffle=False to avoid look-ahead bias# In time series, future data can't inform past predictions
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, shuffle=False
)
print(f"Training samples: {len(X_train)}")
print(f"Testing samples: {len(X_test)}")
# Initialize Random Forest classifier
model = RandomForestClassifier(
n_estimators=100, # 100 decision trees in the forest
max_depth=10, # Prevent trees from growing too deep (overfitting)
random_state=42, # For reproducibility
class_weight='balanced' # Handle imbalanced classes (more ups than downs or vice versa)
)
print("\nTraining model...")
model.fit(X_train, y_train)
print("✓ Model trained")
# ============================================# EVALUATE ON TEST SET# ============================================
y_pred = model.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
print(f"\n{'='*50}")
print(f"TEST SET PERFORMANCE")
print(f"{'='*50}")
print(f"Accuracy: {accuracy:.2%}")
print(f"\nDetailed Classification Report:")
print(classification_report(y_test, y_pred, target_names=['Down', 'Up']))
# ============================================# CROSS-VALIDATION# ============================================# Cross-validation gives us more robust performance estimates# by training/testing on multiple data splits
cv_scores = cross_val_score(model, X_train, y_train, cv=5)
print(f"\nCross-validation scores: {cv_scores}")
print(f"Average CV score: {cv_scores.mean():.2%} (+/- {cv_scores.std():.2%})")
# ============================================# FEATURE IMPORTANCE# ============================================# Random Forests tell us which features mattered most
feature_importance = pd.DataFrame({
'feature': feature_columns,
'importance': model.feature_importances_
}).sort_values('importance', ascending=False)
print(f"\n{'='*50}")
print("FEATURE IMPORTANCE")
print(f"{'='*50}")
print(feature_importance)This script trains a Random Forest classifier on the features we engineered in the previous step. We split our data into training (80%) and testing (20%) sets, with shuffle=False to preserve chronological order. This is critical for time series data because we can't let future information leak into past predictions. The model learns patterns from the training data and then makes predictions on the test set it hasn't seen before. We also run cross-validation, which trains and tests on multiple different data splits to check whether our results are consistent or just lucky.
Interpreting the Results
Machine learning models output a lot of metrics, and interpreting them correctly is essential for understanding whether your bot has a viable strategy. Here's what each output means and what to look for:
Accuracy: The percentage of correct predictions. In trading, 55-60% accuracy is actually quite good—markets are inherently noisy and difficult to predict. Don't expect 90%+ accuracy. If you see numbers that high, your model is likely overfitting or there's a data leakage problem.
Classification report: This breaks down performance for both prediction classes (Up and Down). Precision tells you how often the model is right when it predicts "Up"—important because false positives mean bad trades. Recall tells you what percentage of actual "Up" movements the model caught—important for not missing opportunities. F1-score is the harmonic mean of both, giving you a balanced view of performance.
Cross-validation scores: Training on different subsets of your data reveals whether your model performs consistently or if results vary wildly depending on which specific data points you use. Consistent scores across folds suggest a robust strategy; high variance suggests your model may be learning noise rather than real patterns.
Feature importance: Random Forests tell you which features contributed most to predictions. If
momentumhas the highest importance, your strategy is primarily momentum-driven. Ifvolatilitydominates, you're mostly trading on market instability. This insight helps you understand what your model is actually doing and whether it aligns with your trading thesis.
What counts as a "good" result?
For a trading model, you're looking for accuracy above 55% (better than random guessing at 50%), similar performance on both training and test sets (indicating the model isn't overfitting), and consistent cross-validation scores (suggesting the strategy is robust across different market conditions). If your test accuracy is dramatically lower than training accuracy, the model has memorized the training data rather than learning generalizable patterns.
Step 5: Implement Real-Time Trading Logic
Now let's build the bot's main loop: the logic that fetches current data, makes predictions, and executes trades.
def fetch_current_data():
"""
Fetch the most recent data point for making a prediction.
In production, this runs every N minutes to stay current.
"""
# Get latest block from blockchain
current_block = alchemy.core.get_block_number()
block = alchemy.core.get_block(current_block)
# Get current ETH price (using Alchemy Prices API)
price_url = f"https://eth-mainnet.g.alchemy.com/prices/v1/{os.getenv('ALCHEMY_API_KEY')}/tokens/by-symbol"
price_response = requests.get(
price_url,
params={'symbols': 'ETH'},
headers={'Accept': 'application/json'}
)
current_price = price_response.json()['data'][0]['prices'][0]['value']
return {
'block_number': current_block,
'timestamp': datetime.now(),
'gas_used': block.gas_used,
'transaction_count': len(block.transactions),
'price': current_price
}
def prepare_features_for_prediction(current_data, historical_df):
"""
Engineer features from current data point using historical context.
Why we need historical context: Features like moving averages and
momentum require previous data points to calculate.
"""
# Append current data to our historical DataFrame
temp_df = pd.concat([
historical_df,
pd.DataFrame([current_data])
], ignore_index=True)
# Re-run feature engineering to get current features
temp_df = engineer_features(temp_df)
# Return only the latest row (current features)
return temp_df[feature_columns].iloc[-1:].values
def execute_trade(action, amount, current_price):
"""
Execute a trade. Currently simulated—prints trade details.
In production, replace with:
- Exchange API calls (via CCXT)
- Alchemy Smart Wallet transactions
- Order execution with slippage controls
"""
print(f"\n{'='*50}")
print(f"TRADE SIGNAL: {action.upper()}")
print(f"Amount: {amount} ETH")
print(f"Price: ${current_price:,.2f}")
print(f"Est. Value: ${amount * current_price:,.2f}")
print(f"Timestamp: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"{'='*50}\n")
# TODO: In production, add real execution:# if action == 'BUY':# exchange.create_market_buy_order('ETH/USD', amount)# elif action == 'SELL':# exchange.create_market_sell_order('ETH/USD', amount)
def run_trading_bot(model, historical_df, interval_seconds=300):
"""
Main bot loop: fetch data, predict, execute trades.
This runs continuously, checking market conditions every N seconds.
"""
print("="*60)
print("AI TRADING BOT STARTED")
print("="*60)
print(f"Checking market every {interval_seconds} seconds")
print(f"Model accuracy: {accuracy:.2%}")
print(f"Press Ctrl+C to stop\n")
# Track bot state
position = None # Current position: 'long', 'short', or None
entry_price = 0
trades_executed = 0
while True:
try:
# ============================================# 1. FETCH CURRENT MARKET DATA# ============================================
current_data = fetch_current_data()
current_price = current_data['price']
# ============================================# 2. PREPARE FEATURES & MAKE PREDICTION# ============================================
features = prepare_features_for_prediction(current_data, historical_df)
prediction = model.predict(features)[0]
prediction_proba = model.predict_proba(features)[0]
# Display current state
print(f"[{datetime.now().strftime('%H:%M:%S')}] Price: ${current_price:,.2f} | "
f"Prediction: {'UP' if prediction == 1 else 'DOWN'} "
f"(Confidence: {prediction_proba[prediction]:.1%}) | "
f"Position: {position or 'None'}")
# ============================================# 3. TRADING LOGIC WITH CONFIDENCE THRESHOLD# ============================================
# Only trade when model is confident (>60%)
confidence_threshold = 0.6
# BUY SIGNAL: Predict UP with high confidence, no position
if prediction == 1 and prediction_proba[1] > confidence_threshold:
if position != 'long':
execute_trade('BUY', 0.1, current_price)
position = 'long'
entry_price = current_price
trades_executed += 1
# SELL SIGNAL: Predict DOWN with high confidence, have long position
elif prediction == 0 and prediction_proba[0] > confidence_threshold:
if position == 'long':
profit_pct = ((current_price - entry_price) / entry_price) * 100
print(f"Closing long position. Profit: {profit_pct:+.2f}%")
execute_trade('SELL', 0.1, current_price)
position = None
trades_executed += 1
# ============================================# 4. UPDATE HISTORICAL DATA (ROLLING WINDOW)# ============================================
historical_df = pd.concat([
historical_df,
pd.DataFrame([current_data])
], ignore_index=True).tail(168) # Keep last week
# ============================================# 5. WAIT BEFORE NEXT CHECK# ============================================
time.sleep(interval_seconds)
except KeyboardInterrupt:
print(f"\n{'='*60}")
print("BOT STOPPED BY USER")
print(f"Total trades executed: {trades_executed}")
print(f"{'='*60}")
break
except Exception as e:
print(f"ERROR: {e}")
print("Retrying in 60 seconds...")
time.sleep(60)
# Run the bot (check every 5 minutes)
run_trading_bot(model, df, interval_seconds=300)This step brings everything together into a functional trading bot. The code defines four main functions that work in sequence: fetch_current_data pulls the latest blockchain metrics and ETH price from Alchemy, prepare_features_for_prediction transforms that raw data into the same features our model was trained on, execute_trade handles the actual buy/sell execution (simulated here, but ready for production integration), and run_trading_bot orchestrates everything in a continuous loop that checks market conditions every 5 minutes.
The main trading loop follows a straightforward cycle: fetch current data, generate features, make a prediction, decide whether to trade based on confidence thresholds, update our historical data, and wait before repeating. Here are the key design decisions built into this loop:
Confidence threshold: The bot only executes trades when the model is more than 60% confident in its prediction. This filters out weak signals that are more likely to be noise than genuine opportunities. You can adjust this threshold based on your risk tolerance—higher thresholds mean fewer but more selective trades.
Position tracking: The bot maintains awareness of its current state (long, short, or flat). This prevents illogical behavior like repeatedly buying when you already hold ETH, or selling when you have nothing to sell.
Rolling window: Historical data is capped at the last 168 hours (one week). This prevents memory issues during long-running sessions and ensures the bot's feature calculations stay relevant to current market conditions rather than being skewed by old data.
Error handling: The try/except blocks ensure temporary failures (API timeouts, network issues, rate limits) don't crash the entire bot. Instead, errors are logged and the bot retries after a brief pause.
Graceful shutdown: Pressing Ctrl+C stops the bot cleanly and prints summary statistics, so you can review performance without losing state information.
Why 5-minute intervals? This default balances responsiveness against API rate limits and computational overhead. You can adjust based on your strategy: high-frequency approaches might check every few seconds, while swing trading strategies might only need hourly updates. Shorter intervals capture more opportunities but consume more API calls and require faster execution infrastructure.
Step 6: Add On-Chain Sentiment Analysis with Alchemy
Price and technical indicators only tell part of the story. In crypto, onchain activity—especially large transfers by "whales"—can signal impending price movements before they show up in price data. Whales moving ETH to exchanges often indicates selling pressure, while moving ETH off exchanges suggests accumulation. By monitoring these transfers in real-time, your bot can gain seconds or minutes of advance warning before price reflects the activity.
This step adds whale monitoring as an additional input layer for your trading bot.
In the following two code blocks, we’ll first establish a WebSocket connection through Alchemy to monitor all ETH transfers on the network, filtering for transactions above a defined threshold ETH (100) and categorize them based on whether funds are moving to or from known exchange addresses.
In the second code block, we’ll show you how to feed that whale activity data into your existing trading logic, adjusting confidence thresholds based on whether whales appear to be buying or selling.
Setting up a WebSocket to monitor whale transfers:
def monitor_whale_activity():
"""
Monitor large ETH transfers as sentiment signals using Alchemy WebSockets.
Why this matters:
- Whales moving ETH to exchanges often signals selling pressure
- Whales moving ETH off exchanges signals accumulation (bullish)
- Large transfers between wallets can indicate OTC deals or fund movements
This real-time data can give you seconds or minutes of advance warning
before price reflects the activity.
"""
# Define what constitutes a "whale" transaction
WHALE_THRESHOLD = 100 # 100 ETH (~$200K-400K depending on price)
# Known exchange addresses (for context)
EXCHANGE_ADDRESSES = {
'0x3f5ce5fbfe3e9af3971dd833d26ba9b5c936f0be': 'Binance',
'0x28c6c06298d514db089934071355e5743bf21d60': 'Binance 2',
'0x21a31ee1afc51d94c2efccaa2092ad1028285549': 'Binance 3',
'0xdfd5293d8e347dfe59e90efd55b2956a1343963d': 'Binance 4',
'0x56eddb7aa87536c09ccc2793473599fd21a8b17f': 'Binance 5',
'0x9696f59e4d72e237be84ffd425dcad154bf96976': 'Binance 6',
'0x4e9ce36e442e55ecd9025b9a6e0d88485d628a67': 'Binance 7',
'0xbe0eb53f46cd790cd13851d5eff43d12404d33e8': 'Binance 8',
'0xf977814e90da44bfa03b6295a0616a897441acec': 'Binance 9',
'0x001866ae5b3de6caa5a51543fd9fb64f524f5478': 'Coinbase 1',
'0x71660c4005ba85c37ccec55d0c4493e66fe775d3': 'Coinbase 2',
'0x503828976d22510aad0201ac7ec88293211d23da': 'Coinbase 3',
'0xddfabcdc4d8ffc6d5beaf154f18b778f892a0740': 'Coinbase 4',
'0x3cd751e6b0078be393132286c442345e5dc49699': 'Coinbase 5',
'0xb5d85cbf7cb3ee0d56b3bb207d5fc4b82f43f511': 'Coinbase 6',
'0xeb2629a2734e272bcc07bda959863f316f4bd4cf': 'Coinbase 7',
}
def handle_transfer(event):
"""
Process each transfer event as it happens.
"""
try:
# Extract transfer details
value_wei = int(event['value'], 16)
value_eth = value_wei / 1e18
from_addr = event['from'].lower()
to_addr = event['to'].lower()
tx_hash = event['transactionHash']
# Only process whale-sized transfers
if value_eth >= WHALE_THRESHOLD:
# Determine transfer context
from_exchange = EXCHANGE_ADDRESSES.get(from_addr, None)
to_exchange = EXCHANGE_ADDRESSES.get(to_addr, None)
# Build alert message
alert = f"\n🐋 WHALE ALERT: {value_eth:.2f} ETH"
if from_exchange and not to_exchange:
# Moving OFF exchange = potential buying/holding
alert += f"\n📤 From {from_exchange} to private wallet"
alert += "\n💡 Signal: BULLISH (accumulation)"
elif not from_exchange and to_exchange:
# Moving TO exchange = potential selling
alert += f"\n📥 From private wallet to {to_exchange}"
alert += "\n💡 Signal: BEARISH (potential sell pressure)"
elif from_exchange and to_exchange:
# Exchange to exchange = arbitrage or OTC
alert += f"\n🔄 From {from_exchange} to {to_exchange}"
alert += "\n💡 Signal: NEUTRAL (arbitrage or OTC)"
else:
# Wallet to wallet = unknown intent
alert += f"\n↔️ Between private wallets"
alert += "\n💡 Signal: UNCLEAR (monitor for pattern)"
alert += f"\nTx: https://etherscan.io/tx/{tx_hash}"
print(alert)
# TODO: In production, you might:# - Adjust trading thresholds based on whale activity# - Increase position size if whales are accumulating# - Exit positions early if whales are dumping# - Log whale activity for later analysis
except Exception as e:
print(f"Error processing whale transfer: {e}")
# Subscribe to all ETH transfers on the network# Note: This is resource-intensive. For production, consider:# - Filtering by specific addresses# - Using Alchemy's Transfer API instead# - Subscribing only to specific token contracts
print("\n" + "="*60)
print("WHALE ACTIVITY MONITOR STARTED")
print("="*60)
print(f"Tracking transfers ≥ {WHALE_THRESHOLD} ETH")
print("Press Ctrl+C to stop\n")
try:
# WebSocket filter for ETH transfers# This subscribes to the Transfer event emitted by WETH or native ETH moves
filter_params = {
'address': None, # Monitor all addresses (or specify WETH contract)
'topics': [
# Transfer(address,address,uint256) event signature
alchemy.core.utils.keccak(text="Transfer(address,address,uint256)").hex()
]
}
# Start listening
alchemy.ws.on(filter_params, handle_transfer)
except Exception as e:
print(f"Whale monitor error: {e}")
# Run whale monitor in a separate thread so it doesn't block the main bot
import threading
whale_thread = threading.Thread(target=monitor_whale_activity, daemon=True)
whale_thread.start()
# Now your main trading bot can run alongside the whale monitor# The whale alerts will print in real-time while your bot tradesThe whale monitor above collects the data, but that data needs to feed into your trading bot to actually influence decisions. The following code shows how to track whale sentiment as a running score and adjust your confidence thresholds accordingly, lowering the bar to buy when whales are accumulating, and raising it when they appear to be selling.
# Add this to your trading bot
whale_sentiment = 0 # Global variable tracking recent whale activity
def update_whale_sentiment(signal_type):
"""
Update sentiment based on whale activity.
+1 for bullish moves, -1 for bearish, 0 for neutral.
"""
global whale_sentiment
if signal_type == 'BULLISH':
whale_sentiment += 1
elif signal_type == 'BEARISH':
whale_sentiment -= 1
# Decay over time so old signals matter less
whale_sentiment *= 0.9
# Modify your trading logic to incorporate whale sentiment
def should_trade(prediction, confidence, whale_sentiment):
"""
Enhanced trading logic that considers both ML prediction and whale activity.
"""
base_threshold = 0.6
# If whales are bullish and model predicts up, lower threshold
if prediction == 1 and whale_sentiment > 2:
threshold = base_threshold - 0.1 # More aggressive buying# If whales are bearish, raise threshold (be more cautious)
elif prediction == 1 and whale_sentiment < -2:
threshold = base_threshold + 0.1 # Less aggressive buying
else:
threshold = base_threshold
return confidence > thresholdTogether, these two blocks add an additional signal layer to your trading bot. The first block runs in a background thread, continuously listening for large ETH transfers via WebSocket and categorizing each one as bullish (moving off exchanges), bearish (moving to exchanges), or neutral. The second block maintains a running sentiment score based on that whale activity and modifies your trading thresholds accordingly.
When your ML model's prediction aligns with whale behavior—for example, predicting price increase while whales are accumulating, the combined signal is stronger, and the bot trades more aggressively. When signals conflict, the bot becomes more cautious.
This matters because institutional and large holders often have better information or longer time horizons than retail traders. Big moves to or from exchanges affect available supply and can precede price action by minutes to hours. By incorporating onchain activity alongside your ML model's predictions, you're giving your bot a more complete picture of market conditions than price data alone can provide.
Step 7: Implement Risk Management
This is arguably the most important step in the entire tutorial. Even the best predictive model will lose money without proper risk management. A bot that's right 60% of the time can still go bankrupt if the 40% of losing trades aren't controlled—one bad position can wipe out dozens of winners. Risk management serves several critical functions: it limits how much you can lose on any single trade (stop losses), locks in gains before the market reverses (take profit), prevents catastrophic portfolio decline (drawdown limits), and stops emotional overtrading (daily trade limits). The code below implements all of these as a reusable RiskManager class that integrates with your trading bot.
Building the RiskManager class
The following class encapsulates all risk management logic in one place. It tracks your portfolio value, monitors for dangerous conditions, calculates appropriate position sizes based on confidence, and records every trade for later analysis.
class RiskManager:
"""
Comprehensive risk management for trading bots.
Risk management is what separates profitable bots from bankrupt ones.
Key principles:
1. Never risk more than you can afford to lose
2. Cut losses quickly, let winners run
3. Position size matters more than win rate
4. Track everything for post-mortem analysis
"""
def __init__(self,
initial_capital=10000,
max_position_size=1.0,
max_drawdown=0.15,
stop_loss=0.05,
take_profit=0.10,
max_daily_trades=20):
"""
Initialize risk parameters.
Args:
initial_capital: Starting portfolio value in USD
max_position_size: Maximum ETH per trade
max_drawdown: Max portfolio decline before pausing (15% = 0.15)
stop_loss: Per-trade loss limit (5% = 0.05)
take_profit: Per-trade profit target (10% = 0.10)
max_daily_trades: Prevent overtrading
"""
self.initial_capital = initial_capital
self.portfolio_value = initial_capital
self.peak_value = initial_capital
self.max_position_size = max_position_size
self.max_drawdown = max_drawdown
self.stop_loss = stop_loss
self.take_profit = take_profit
self.max_daily_trades = max_daily_trades
self.trades = []
self.daily_trades = 0
self.last_trade_date = datetime.now().date()
self.trading_paused = False
def check_drawdown(self):
"""
Pause trading if drawdown exceeds threshold.
Drawdown is measured from peak portfolio value.
This prevents catastrophic losses during bad market conditions.
"""
current_drawdown = (self.peak_value - self.portfolio_value) / self.peak_value
if current_drawdown >= self.max_drawdown:
self.trading_paused = True
print(f"\n{'='*60}")
print(f"⚠️ MAX DRAWDOWN REACHED: {current_drawdown:.2%}")
print(f"Portfolio: ${self.portfolio_value:,.2f} (down from ${self.peak_value:,.2f})")
print(f"Trading PAUSED. Manual review required.")
print(f"{'='*60}\n")
return False
return True
def calculate_position_size(self, confidence, current_price):
"""
Adjust position size based on prediction confidence and portfolio size.
This implements a simplified Kelly Criterion approach:
- Higher confidence = larger position
- Never exceed max position size
- Scale down if portfolio has shrunk
Args:
confidence: Model's prediction probability (0-1)
current_price: Current ETH price for value calculation
Returns:
Position size in ETH
"""
# Base size scaled by confidence# At 60% confidence: 0.6 * max_size# At 90% confidence: 0.9 * max_size
base_size = self.max_position_size * confidence
# Scale down if portfolio has lost value
portfolio_scale = self.portfolio_value / self.initial_capital
adjusted_size = base_size * portfolio_scale
# Never exceed max position size
final_size = min(adjusted_size, self.max_position_size)
# Verify we can afford this trade
trade_value = final_size * current_price
if trade_value > self.portfolio_value * 0.95: # Never use >95% of portfolio
final_size = (self.portfolio_value * 0.95) / current_price
return round(final_size, 4) # Round to 4 decimals
def check_stop_loss(self, entry_price, current_price, position_type):
"""
Check if stop loss is triggered for current position.
Stop losses protect against large individual trade losses.
Even if your model is right 60% of the time, the 40% losers
can wipe you out without stops.
"""
if position_type == 'long':
loss = (entry_price - current_price) / entry_price
if loss >= self.stop_loss:
print(f"\n🛑 STOP LOSS TRIGGERED")
print(f"Entry: ${entry_price:,.2f} → Current: ${current_price:,.2f}")
print(f"Loss: {loss:.2%}")
return True
return False
def check_take_profit(self, entry_price, current_price, position_type):
"""
Check if take profit target is reached.
Taking profits locks in gains and prevents giving back winnings
if the market reverses.
"""
if position_type == 'long':
profit = (current_price - entry_price) / entry_price
if profit >= self.take_profit:
print(f"\n🎯 TAKE PROFIT TARGET REACHED")
print(f"Entry: ${entry_price:,.2f} → Current: ${current_price:,.2f}")
print(f"Profit: {profit:.2%}")
return True
return False
def check_daily_limit(self):
"""
Prevent overtrading by limiting daily trades.
Overtrading leads to:
- Death by a thousand fees
- Emotional decision-making
- Curve-fitting to noise rather than signal
"""
today = datetime.now().date()
# Reset counter at start of new day
if today != self.last_trade_date:
self.daily_trades = 0
self.last_trade_date = today
if self.daily_trades >= self.max_daily_trades:
print(f"⚠️ Daily trade limit reached ({self.max_daily_trades})")
return False
return True
def can_trade(self):
"""
Master check: Can we trade right now?
"""
if self.trading_paused:
return False
if not self.check_drawdown():
return False
if not self.check_daily_limit():
return False
return True
def record_trade(self, trade_type, price, amount, profit_loss=0):
"""
Track all trades for analysis and performance monitoring.
This data is gold for:
- Understanding what's working/not working
- Tax reporting
- Strategy refinement
- Performance attribution
"""
self.trades.append({
'timestamp': datetime.now(),
'type': trade_type,
'price': price,
'amount': amount,
'value': price * amount,
'profit_loss': profit_loss,
'portfolio_value': self.portfolio_value
})
# Update portfolio value
self.portfolio_value += profit_loss
self.peak_value = max(self.peak_value, self.portfolio_value)
# Increment daily trade counter
self.daily_trades += 1
def get_performance_stats(self):
"""
Calculate comprehensive performance metrics.
"""
if not self.trades:
return "No trades executed yet"
df = pd.DataFrame(self.trades)
# Separate buys and sells
buys = df[df['type'] == 'BUY']
sells = df[df['type'] == 'SELL']
# Calculate returns
total_return = ((self.portfolio_value - self.initial_capital) / self.initial_capital) * 100
# Win rate (profitable trades / total trades)
profitable_trades = len(sells[sells['profit_loss'] > 0])
total_closed_trades = len(sells)
win_rate = (profitable_trades / total_closed_trades * 100) if total_closed_trades > 0 else 0
# Average profit per winning trade
avg_win = sells[sells['profit_loss'] > 0]['profit_loss'].mean() if profitable_trades > 0 else 0
# Average loss per losing trade
losing_trades = sells[sells['profit_loss'] < 0]
avg_loss = losing_trades['profit_loss'].mean() if len(losing_trades) > 0 else 0
# Profit factor (total wins / total losses)
total_wins = sells[sells['profit_loss'] > 0]['profit_loss'].sum()
total_losses = abs(sells[sells['profit_loss'] < 0]['profit_loss'].sum())
profit_factor = total_wins / total_losses if total_losses > 0 else float('inf')
# Current drawdown
current_drawdown = ((self.peak_value - self.portfolio_value) / self.peak_value) * 100
stats = f"""
{'='*60}
PERFORMANCE STATISTICS
{'='*60}
Portfolio Value: ${self.portfolio_value:,.2f}
Initial Capital: ${self.initial_capital:,.2f}
Total Return: {total_return:+.2f}%
Peak Value: ${self.peak_value:,.2f}
Current Drawdown: {current_drawdown:.2f}%
Trading Activity:
- Total Trades: {len(self.trades)}
- Closed Trades: {total_closed_trades}
- Win Rate: {win_rate:.1f}%
- Profitable Trades: {profitable_trades}
- Losing Trades: {len(losing_trades)}
Trade Quality:
- Average Win: ${avg_win:,.2f}
- Average Loss: ${avg_loss:,.2f}
- Profit Factor: {profit_factor:.2f}x
- Total Wins: ${total_wins:,.2f}
- Total Losses: ${total_losses:,.2f}
Risk Status:
- Trading Paused: {self.trading_paused}
- Daily Trades: {self.daily_trades}/{self.max_daily_trades}
{'='*60}
"""
return stats
# Initialize risk manager
risk_manager = RiskManager(
initial_capital=10000,
max_position_size=0.5, # 0.5 ETH max per trade
max_drawdown=0.15, # Stop at 15% portfolio loss
stop_loss=0.03, # 3% stop loss per trade
take_profit=0.06, # 6% take profit target
max_daily_trades=15 # Max 15 trades per day
)The RiskManager class centralizes all risk controls into a single, reusable component. When initialized, you define your risk parameters: starting capital, maximum position size per trade, the portfolio drawdown percentage that triggers a trading pause, stop loss and take profit thresholds for individual trades, and a daily trade limit to prevent overtrading.
The class provides several key methods. check_drawdown monitors whether your portfolio has declined too far from its peak value and automatically pauses trading if the threshold is breached—this prevents a losing streak from draining your entire account. calculate_position_size implements a simplified Kelly Criterion approach, sizing positions based on model confidence (higher confidence means larger positions) while scaling down if the portfolio has already taken losses.
check_stop_loss and check_take_profit monitor open positions and trigger exits when price moves beyond your defined thresholds. record_trade logs every transaction for later analysis, and get_performance_stats calculates comprehensive metrics like win rate, profit factor, and current drawdown.
Integrating Risk Management Into Your Bot
With the RiskManager class defined, the next step is wiring it into your main trading loop. The following code modifies the bot from Step 5 to check risk limits before every trade, use dynamic position sizing based on confidence, and automatically exit positions when stop loss or take profit levels are hit.
# Modified trading loop with risk management
def run_trading_bot_with_risk_management(model, historical_df, risk_manager, interval_seconds=300):
"""
Enhanced trading bot with comprehensive risk management.
"""
print("="*60)
print("AI TRADING BOT WITH RISK MANAGEMENT")
print("="*60)
print(f"Initial Capital: ${risk_manager.initial_capital:,.2f}")
print(f"Max Position: {risk_manager.max_position_size} ETH")
print(f"Stop Loss: {risk_manager.stop_loss:.1%}")
print(f"Max Drawdown: {risk_manager.max_drawdown:.1%}")
print("="*60 + "\n")
position = None
entry_price = 0
position_size = 0
while True:
try:
# Check if we can trade
if not risk_manager.can_trade():
print("Trading paused due to risk limits")
time.sleep(interval_seconds)
continue
# Fetch data and predict
current_data = fetch_current_data()
current_price = current_data['price']
features = prepare_features_for_prediction(current_data, historical_df)
prediction = model.predict(features)[0]
prediction_proba = model.predict_proba(features)[0]
confidence = prediction_proba[prediction]
# Display status
print(f"[{datetime.now().strftime('%H:%M:%S')}] "
f"Price: ${current_price:,.2f} | "
f"Prediction: {'UP' if prediction == 1 else 'DOWN'} ({confidence:.1%}) | "
f"Portfolio: ${risk_manager.portfolio_value:,.2f} | "
f"Position: {position or 'None'}")
# Check stop loss and take profit for open positions
if position == 'long':
if risk_manager.check_stop_loss(entry_price, current_price, 'long'):
# STOP LOSS HIT
loss = (entry_price - current_price) * position_size
execute_trade('SELL (Stop Loss)', position_size, current_price)
risk_manager.record_trade('SELL', current_price, position_size, loss)
position = None
continue
elif risk_manager.check_take_profit(entry_price, current_price, 'long'):
# TAKE PROFIT HIT
profit = (current_price - entry_price) * position_size
execute_trade('SELL (Take Profit)', position_size, current_price)
risk_manager.record_trade('SELL', current_price, position_size, profit)
position = None
continue
# Regular trading logic
if prediction == 1 and confidence > 0.6:
if position != 'long':
# Calculate position size based on confidence
position_size = risk_manager.calculate_position_size(confidence, current_price)
execute_trade('BUY', position_size, current_price)
risk_manager.record_trade('BUY', current_price, position_size)
position = 'long'
entry_price = current_price
elif prediction == 0 and confidence > 0.6:
if position == 'long':
# Close position
profit = (current_price - entry_price) * position_size
execute_trade('SELL', position_size, current_price)
risk_manager.record_trade('SELL', current_price, position_size, profit)
position = None
# Update historical data
historical_df = pd.concat([
historical_df,
pd.DataFrame([current_data])
], ignore_index=True).tail(168)
time.sleep(interval_seconds)
except KeyboardInterrupt:
print("\n" + risk_manager.get_performance_stats())
break
except Exception as e:
print(f"ERROR: {e}")
time.sleep(60)
# Run bot with risk management
run_trading_bot_with_risk_management(model, df, risk_manager, interval_seconds=300)This enhanced trading loop adds several layers of protection compared to the basic version from Step 5. Before each iteration, the bot checks risk_manager.can_trade() to verify that no risk limits have been breached—if the portfolio has hit max drawdown or the daily trade limit is exhausted, trading pauses automatically.
For open positions, the bot continuously monitors whether stop loss or take profit thresholds have been hit and exits immediately if triggered, without waiting for the model to generate a new signal. Position sizes are now dynamic, calculated by the risk manager based on model confidence and current portfolio value rather than using a fixed amount.
The result is a bot that protects capital during losing streaks, locks in profits automatically, and scales its risk exposure based on how well it's performing. When you stop the bot, it prints comprehensive performance statistics including total return, win rate, average win/loss size, and profit factor—giving you the data needed to evaluate and refine your strategy.
Step 8: Backtesting Your Strategy
Before risking real money, backtest your strategy on historical data. This reveals how your bot would have performed in different market conditions.
The following function simulates your entire trading strategy against historical data, including realistic fee and slippage estimates, stop loss and take profit execution, and detailed performance tracking. It outputs key metrics and generates an equity curve visualization showing how your portfolio value would have evolved over time.
def backtest_strategy(model, df, risk_manager, initial_capital=10000):
"""
Comprehensive backtest with risk management.
Backtesting answers critical questions:
- Would this strategy have been profitable?
- What's the maximum drawdown I need to stomach?
- How many trades does it generate?
- Does it work in different market conditions?
CRITICAL: Backtests are optimistic. Real trading includes:
- Slippage (getting worse prices than expected)
- Fees (exchange commissions, gas costs)
- Execution delays (orders take time to fill)
- Market impact (your trades affect prices)
Assume real performance will be 20-30% worse than backtest.
"""
print("\n" + "="*60)
print("BACKTESTING STRATEGY")
print("="*60)
print(f"Data points: {len(df)}")
print(f"Time period: {df['timestamp'].min()} to {df['timestamp'].max()}")
print(f"Initial capital: ${initial_capital:,.2f}\n")
# Reset risk manager for clean backtest
capital = initial_capital
position = None
entry_price = 0
position_size = 0
trades = []
portfolio_values = []
# Track statistics
wins = 0
losses = 0
total_fees = 0
max_drawdown = 0
peak_value = initial_capital
# Simulation parameters
FEE_RATE = 0.001 # 0.1% per trade (typical exchange fee)
SLIPPAGE = 0.0005 # 0.05% slippage (market orders get worse prices)
for i in range(len(df)):
if i < 24: # Need history for features
continue
current_price = df['price'].iloc[i]
timestamp = df['timestamp'].iloc[i]
# Get features and prediction
features = df[feature_columns].iloc[i:i+1].values
prediction = model.predict(features)[0]
prediction_proba = model.predict_proba(features)[0]
confidence = prediction_proba[prediction]
# Check stop loss and take profit for open positions
if position == 'long':
current_pnl_pct = (current_price - entry_price) / entry_price
# Stop loss hit
if current_pnl_pct <= -risk_manager.stop_loss:
sell_price = current_price * (1 - SLIPPAGE) # Slippage on market sell
pnl = (sell_price - entry_price) * position_size
fees = sell_price * position_size * FEE_RATE
net_pnl = pnl - fees
capital += (position_size * sell_price) - fees
total_fees += fees
losses += 1
trades.append({
'timestamp': timestamp,
'type': 'SELL (Stop)',
'price': sell_price,
'amount': position_size,
'pnl': net_pnl,
'capital': capital
})
position = None
continue
# Take profit hit
elif current_pnl_pct >= risk_manager.take_profit:
sell_price = current_price * (1 - SLIPPAGE)
pnl = (sell_price - entry_price) * position_size
fees = sell_price * position_size * FEE_RATE
net_pnl = pnl - fees
capital += (position_size * sell_price) - fees
total_fees += fees
wins += 1
trades.append({
'timestamp': timestamp,
'type': 'SELL (Profit)',
'price': sell_price,
'amount': position_size,
'pnl': net_pnl,
'capital': capital
})
position = None
continue
# Regular trading logic
if prediction == 1 and confidence > 0.6:
if position != 'long' and capital > 0:
# BUY
buy_price = current_price * (1 + SLIPPAGE) # Slippage on market buy
position_size = min(
risk_manager.max_position_size,
(capital * 0.95) / buy_price # Use max 95% of capital
)
cost = position_size * buy_price
fees = cost * FEE_RATE
capital -= (cost + fees)
total_fees += fees
entry_price = buy_price
position = 'long'
trades.append({
'timestamp': timestamp,
'type': 'BUY',
'price': buy_price,
'amount': position_size,
'pnl': -fees, # Fees are a loss
'capital': capital
})
elif prediction == 0 and confidence > 0.6:
if position == 'long':
# SELL
sell_price = current_price * (1 - SLIPPAGE)
pnl = (sell_price - entry_price) * position_size
fees = sell_price * position_size * FEE_RATE
net_pnl = pnl - fees
capital += (position_size * sell_price) - fees
total_fees += fees
if net_pnl > 0:
wins += 1
else:
losses += 1
trades.append({
'timestamp': timestamp,
'type': 'SELL',
'price': sell_price,
'amount': position_size,
'pnl': net_pnl,
'capital': capital
})
position = None
# Track portfolio value (including open position)
if position == 'long':
current_value = capital + (position_size * current_price)
else:
current_value = capital
portfolio_values.append({
'timestamp': timestamp,
'value': current_value
})
# Track drawdown
peak_value = max(peak_value, current_value)
current_drawdown = (peak_value - current_value) / peak_value
max_drawdown = max(max_drawdown, current_drawdown)
# Close any open position at end
if position == 'long':
final_price = df['price'].iloc[-1] * (1 - SLIPPAGE)
pnl = (final_price - entry_price) * position_size
fees = final_price * position_size * FEE_RATE
capital += (position_size * final_price) - fees
total_fees += fees
# Calculate final statistics
total_return = ((capital - initial_capital) / initial_capital) * 100
total_trades = len([t for t in trades if t['type'] == 'BUY'])
win_rate = (wins / (wins + losses) * 100) if (wins + losses) > 0 else 0
# Print results
print("\n" + "="*60)
print("BACKTEST RESULTS")
print("="*60)
print(f"\nCapital:")
print(f" Initial: ${initial_capital:,.2f}")
print(f" Final: ${capital:,.2f}")
print(f" Total Return: {total_return:+.2f}%")
print(f" Max Drawdown: {max_drawdown:.2%}")
print(f"\nTrading Activity:")
print(f" Total Trades: {total_trades}")
print(f" Winning Trades: {wins}")
print(f" Losing Trades: {losses}")
print(f" Win Rate: {win_rate:.1f}%")
print(f" Total Fees Paid: ${total_fees:,.2f}")
if wins > 0 and losses > 0:
avg_win = sum(t['pnl'] for t in trades if t['pnl'] > 0) / wins
avg_loss = sum(t['pnl'] for t in trades if t['pnl'] < 0) / losses
profit_factor = abs(sum(t['pnl'] for t in trades if t['pnl'] > 0) /
sum(t['pnl'] for t in trades if t['pnl'] < 0))
print(f"\nTrade Quality:")
print(f" Avg Win: ${avg_win:,.2f}")
print(f" Avg Loss: ${avg_loss:,.2f}")
print(f" Profit Factor: {profit_factor:.2f}x")
print("="*60 + "\n")
# Plot equity curve
import matplotlib.pyplot as plt
pv_df = pd.DataFrame(portfolio_values)
plt.figure(figsize=(12, 6))
plt.plot(pv_df['timestamp'], pv_df['value'])
plt.axhline(y=initial_capital, color='r', linestyle='--', label='Initial Capital')
plt.title('Portfolio Value Over Time (Backtest)')
plt.xlabel('Date')
plt.ylabel('Portfolio Value ($)')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('backtest_equity_curve.png')
print("📊 Equity curve saved to 'backtest_equity_curve.png'\n")
return trades, capital
# Run comprehensive backtest
trades, final_capital = backtest_strategy(model, df, risk_manager, initial_capital=10000)This function replays your entire trading strategy against the historical data you've collected, simulating every buy and sell decision your bot would have made. It iterates through each time period in your dataset, generates predictions using your trained model, checks whether stop loss or take profit thresholds are hit for open positions, executes trades when confidence exceeds your threshold, and tracks portfolio value throughout.
The simulation includes realistic friction: a 0.1% fee on every trade (typical for major exchanges) and 0.05% slippage (the difference between expected and actual execution price when using market orders). These small percentages compound quickly over many trades, which is why the function tracks total fees paid, often the silent killer of otherwise profitable strategies.
At the end, the function outputs comprehensive statistics and saves an equity curve visualization showing how your portfolio value would have evolved over the testing period.
Interpreting Backtest Results
The backtest outputs several metrics that help you evaluate whether your strategy is viable. Here's what each one means and what to look for:
Total return: Your strategy's profitability. Anything above 0% is profitable, but you should compare those results to buy-and-hold ETH.
Max drawdown: The worst peak-to-trough decline. If you can't stomach a 15% loss, you can't run this strategy.
Win rate: Percentage of profitable trades. You can be profitable with <50% win rate if your wins are bigger than your losses.
Profit factor: Total wins / total losses. Above 1.5x is generally good; below 1.0x means you're losing money.
Total fees: Often the silent killer. High-frequency strategies can be profitable before fees but losers after.
Reality check: if your backtest shows 100%+ returns, you probably have a bug (look-ahead bias, overfitting, etc.). Real trading will be worse than backtests due to slippage, fees, execution delays, and unforeseen events.
Congratulations, if you run this - you should see that you've built a functional AI trading bot!
What You've Built
You now have a working trading bot that:
Fetches real-time blockchain data using Alchemy's infrastructure
Combines multiple data sources: prices, on-chain metrics, whale activity
Uses machine learning to predict short-term price movements
Manages risk with stop losses, position sizing, and drawdown limits
Can be backtested to validate strategies before going live
This is a primitive but complete system. It demonstrates the core workflow of any AI trading bot, from data collection to execution.
Critical Notes and Warnings
Before you launch your AI trading bot, please be mindful of the following to protect your capital:
Start with paper trading: Run your bot with simulated orders for at least a month. Track hypothetical performance to identify bugs and refine your strategy without risking capital.
Use small positions: When you do go live, start with 10-20% of your intended capital. Even well-tested bots can behave unexpectedly in production.
Monitor constantly: Especially in the first weeks, watch your bot closely. Set up alerts for unusual behavior (too many trades, large losses, errors).
Have an automated kill switch: Build in mechanisms that halt trading automatically when critical thresholds are breached, maximum daily loss, unusual volatility spikes, or system errors. Markets can turn against you faster than you can manually intervene, so relying on manual shutdown isn't enough. Your bot should be able to protect itself even when you're asleep or away from your desk.
Mind the fees: Trading on DEXs incurs gas costs; CEXs charge trading fees. High-frequency strategies can be profitable before fees but losers after. Always include realistic fee estimates.
Regulatory compliance: Understand the laws in your jurisdiction. Some regions require licensing for algorithmic trading. If managing others' money, securities regulations likely apply.
Tax implications: Every trade is a taxable event in many jurisdictions. Keep meticulous records. Your bot's 100 trades per day could create a tax reporting nightmare.
Security is paramount:
Never hardcode API keys
Use environment variables
Enable 2FA on all accounts
Consider using Smart Wallets with spending limits
Keep private keys offline when possible
Regularly audit your code for vulnerabilities
Conclusion
The tools and techniques covered in this guide, fetching real-time blockchain data, engineering predictive features, training ML models, implementing risk management, and backtesting strategies, provide a foundation for building AI trading systems. But this is a starting point, not a finished product. Real-world trading requires continuous iteration: refining models as market conditions change, stress-testing against edge cases, and building robust infrastructure that can run reliably 24/7.
Ready to start building? Sign up for Alchemy to get your API key and begin experimenting with the code examples above. For structured learning on blockchain development, check out Alchemy University.
Alchemy Newsletter
Be the first to know about releases
Sign up for our newsletter
Get the latest product updates and resources from Alchemy
By entering your email address, you agree to receive our marketing communications and product updates. You acknowledge that Alchemy processes the information we receive in accordance with our Privacy Notice. You can unsubscribe anytime.
Related articles

From One Node to Cortex: Serving Blockchain Data at 100K TPS, Globally

What Is the Ethereum Fusaka Upgrade? Dev Guide to 12 EIPs
A practical breakdown of the Fusaka upgrade, explaining the 12 core EIPs and how they change data availability, cryptography, gas costs, and validator operations across the Ethereum stack.

How to Make a Solana Wallet: A Technical Guide
A walkthrough of Solana wallets covering the basics and how to steps on configuring your own basic and smart wallet.