Want to create your own trading bot?
In this guide, you’ll learn how to build a smart trading system from scratch using Python.
We’ll create a complete portfolio manager that can fetch real stock data,
make trading decisions, and test strategies before risking real money.
Whether you’re a programmer curious about algorithmic trading or an
investor looking to automate your strategies, this guide will show you
how.
All you need is basic Python knowledge and some understanding of stock trading concepts.
Setting Up the Environment
First, let’s install the necessary packages:
```bash
pip install yfinance pandas numpy matplotlib scikit-learn
```
## 1. Data Acquisition and Preparation
We'll start by creating a function to fetch historical stock data:
```python
import yfinance as yf
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
def get_stock_data(tickers, start_date, end_date):
"""
Fetch historical stock data for multiple tickers
Parameters:
tickers (list): List of stock ticker symbols
start_date (str): Start date in 'YYYY-MM-DD' format
end_date (str): End date in 'YYYY-MM-DD' format
Returns:
dict: Dictionary containing DataFrames for each ticker
"""
stock_data = {}
for ticker in tickers:
try:
stock = yf.Ticker(ticker)
df = stock.history(start=start_date, end=end_date)
if not df.empty:
stock_data[ticker] = df
else:
print(f"No data found for {ticker}")
except Exception as e:
print(f"Error fetching data for {ticker}: {e}")
return stock_data
# Example usage
tickers = ['AAPL', 'MSFT', 'GOOGL', 'AMZN']
start_date = '2020–01–01'
end_date = '2023–12–31'
stock_data = get_stock_data(tickers, start_date, end_date)
```
2. Building Trading Strategies
Let’s implement two common trading strategies: Moving Average Crossover and RSI (Relative Strength Index).
2.1 Moving Average Crossover Strategy
```python
def calculate_moving_averages(df, short_window=20, long_window=50):
"""
Calculate short and long moving averages and generate trading signals
"""
df = df.copy()
df['SMA_short'] = df['Close'].rolling(window=short_window).mean()
df['SMA_long'] = df['Close'].rolling(window=long_window).mean()
# Generate trading signals
df['Signal'] = 0
df.loc[df['SMA_short'] > df['SMA_long'], 'Signal'] = 1 # Buy signal
df.loc[df['SMA_short'] < df['SMA_long'], 'Signal'] = -1 # Sell signal
return df
def implement_ma_strategy(stock_data, short_window=20, long_window=50):
"""
Implement Moving Average Crossover strategy for multiple stocks
"""
strategy_data = {}
for ticker, df in stock_data.items():
strategy_data[ticker] = calculate_moving_averages(df, short_window, long_window)
return strategy_data
```
2.2 RSI Strategy
```python
def calculate_rsi(df, period=14):
"""
Calculate RSI and generate trading signals
"""
df = df.copy()
# Calculate price changes
delta = df['Close'].diff()
# Calculate gains (up) and losses (down)
gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
# Calculate RSI
rs = gain / loss
df['RSI'] = 100 - (100 / (1 + rs))
# Generate trading signals
df['Signal'] = 0
df.loc[df['RSI'] < 30, 'Signal'] = 1 # Oversold - Buy signal
df.loc[df['RSI'] > 70, 'Signal'] = -1 # Overbought - Sell signal
return df
def implement_rsi_strategy(stock_data, period=14):
"""
Implement RSI strategy for multiple stocks
"""
strategy_data = {}
for ticker, df in stock_data.items():
strategy_data[ticker] = calculate_rsi(df, period)
return strategy_data
```
## 3. Portfolio Management
Let's create a Portfolio class to manage our investments and track performance:
```python
class Portfolio:
def __init__(self, initial_capital=100000):
self.initial_capital = initial_capital
self.cash = initial_capital
self.positions = {}
self.portfolio_value_history = []
self.transaction_history = []
def execute_trade(self, ticker, shares, price, date, signal):
"""
Execute a trade and update portfolio
"""
trade_value = shares * price
if signal == 1: # Buy
if self.cash >= trade_value:
self.cash -= trade_value
self.positions[ticker] = self.positions.get(ticker, 0) + shares
self.transaction_history.append({
'date': date,
'ticker': ticker,
'action': 'BUY',
'shares': shares,
'price': price,
'value': trade_value
})
return True
return False
elif signal == -1: # Sell
if self.positions.get(ticker, 0) >= shares:
self.cash += trade_value
self.positions[ticker] = self.positions.get(ticker, 0) - shares
self.transaction_history.append({
'date': date,
'ticker': ticker,
'action': 'SELL',
'shares': shares,
'price': price,
'value': trade_value
})
return True
return False
def calculate_portfolio_value(self, date, stock_data):
"""
Calculate total portfolio value at a given date
"""
total_value = self.cash
for ticker, shares in self.positions.items():
if ticker in stock_data and not stock_data[ticker].empty:
price = stock_data[ticker].loc[date, 'Close']
total_value += shares * price
self.portfolio_value_history.append({
'date': date,
'value': total_value
})
return total_value
```
3. Portfolio Management
Let’s create a Portfolio class to manage our investments and track performance:
```python
class Portfolio:
def __init__(self, initial_capital=100000):
self.initial_capital = initial_capital
self.cash = initial_capital
self.positions = {}
self.portfolio_value_history = []
self.transaction_history = []
def execute_trade(self, ticker, shares, price, date, signal):
"""
Execute a trade and update portfolio
"""
trade_value = shares * price
if signal == 1: # Buy
if self.cash >= trade_value:
self.cash -= trade_value
self.positions[ticker] = self.positions.get(ticker, 0) + shares
self.transaction_history.append({
'date': date,
'ticker': ticker,
'action': 'BUY',
'shares': shares,
'price': price,
'value': trade_value
})
return True
return False
elif signal == -1: # Sell
if self.positions.get(ticker, 0) >= shares:
self.cash += trade_value
self.positions[ticker] = self.positions.get(ticker, 0) - shares
self.transaction_history.append({
'date': date,
'ticker': ticker,
'action': 'SELL',
'shares': shares,
'price': price,
'value': trade_value
})
return True
return False
def calculate_portfolio_value(self, date, stock_data):
"""
Calculate total portfolio value at a given date
"""
total_value = self.cash
for ticker, shares in self.positions.items():
if ticker in stock_data and not stock_data[ticker].empty:
price = stock_data[ticker].loc[date, 'Close']
total_value += shares * price
self.portfolio_value_history.append({
'date': date,
'value': total_value
})
return total_value
```
4. Backtesting Framework
Now let’s create a backtesting framework to evaluate our strategies:
```python
class Backtester:
def __init__(self, strategy_data, initial_capital=100000):
self.strategy_data = strategy_data
self.portfolio = Portfolio(initial_capital)
self.performance_metrics = {}
def run_backtest(self, position_size=0.1):
"""
Run backtest on the strategy data
Parameters:
position_size (float): Fraction of portfolio to allocate per trade
"""
dates = sorted(set.intersection(*[set(df.index) for df in self.strategy_data.values()]))
for date in dates:
# Execute trades based on signals
for ticker, df in self.strategy_data.items():
signal = df.loc[date, 'Signal']
if signal != 0:
portfolio_value = self.portfolio.calculate_portfolio_value(date, self.strategy_data)
trade_value = portfolio_value * position_size
price = df.loc[date, 'Close']
shares = int(trade_value / price)
if shares > 0:
self.portfolio.execute_trade(ticker, shares, price, date, signal)
# Update portfolio value
self.portfolio.calculate_portfolio_value(date, self.strategy_data)
self.calculate_performance_metrics()
def calculate_performance_metrics(self):
"""
Calculate various performance metrics
"""
portfolio_values = pd.DataFrame(self.portfolio.portfolio_value_history)
portfolio_values.set_index('date', inplace=True)
returns = portfolio_values['value'].pct_change()
self.performance_metrics = {
'total_return': (portfolio_values['value'].iloc[-1] / self.portfolio.initial_capital - 1) * 100,
'sharpe_ratio': np.sqrt(252) * returns.mean() / returns.std(),
'max_drawdown': (portfolio_values['value'] / portfolio_values['value'].cummax() - 1).min() * 100,
'volatility': returns.std() * np.sqrt(252) * 100
}
def plot_performance(self):
"""
Plot portfolio performance
"""
import matplotlib.pyplot as plt
portfolio_values = pd.DataFrame(self.portfolio.portfolio_value_history)
plt.figure(figsize=(12, 6))
plt.plot(portfolio_values['date'], portfolio_values['value'])
plt.title('Portfolio Value Over Time')
plt.xlabel('Date')
plt.ylabel('Portfolio Value ($)')
plt.grid(True)
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
```
5. Running a Complete Backtest
Here’s how to put it all together:
```python
# Initialize data and strategies
tickers = ['AAPL', 'MSFT', 'GOOGL', 'AMZN']
start_date = '2020–01–01'
end_date = '2023–12–31'
# Fetch data
stock_data = get_stock_data(tickers, start_date, end_date)
# Implement strategy (using Moving Average Crossover)
strategy_data = implement_ma_strategy(stock_data)
# Create and run backtest
backtester = Backtester(strategy_data)
backtester.run_backtest(position_size=0.1)
# Print performance metrics
print("\nPerformance Metrics:")
for metric, value in backtester.performance_metrics.items():
print(f"{metric}: {value:.2f}")
# Plot performance
backtester.plot_performance()
```
6. Analyzing Results
To analyze your backtest results, consider the following metrics:
1. Total Return: Overall portfolio performance
2. Sharpe Ratio: Risk-adjusted return (higher is better)
3. Maximum Drawdown: Largest peak-to-trough decline
4. Volatility: Portfolio price fluctuation
Additionally, you might want to analyze:
- Trade success rate
- Average profit per trade
- Position holding periods
- Transaction costs impact
7. Important Considerations
When implementing your trading strategy, keep in mind:
1. Transaction Costs: Real-world trading involves commissions and spreads
2. Slippage: You may not always get the exact price you want
3. Liquidity: Ensure your position sizes are realistic for the market
4. Look-Ahead Bias: Avoid using future data in your strategy
5. Survivorship Bias: Consider delisted stocks in historical analysis
6. Market Impact: Large trades can affect market prices
Conclusion
This guide provides a foundation for building and testing trading
strategies. Remember that past performance doesn’t guarantee future
results, and it’s important to thoroughly test strategies under
different market conditions before deploying them with real money.
For further improvements, consider:
- Adding more sophisticated strategies
- Implementing risk management rules
- Optimizing strategy parameters
- Adding transaction costs and slippage
- Implementing portfolio rebalancing
- Adding more detailed performance analytics