How to do Algorithmic Trading with Python?

Algorithmic trading, or algo trading, is simply a way to use computer programs to make trading decisions automatically. In this overview, we’ll explore the basics of algorithmic trading, focusing on Python as the language to make it happen.

When we talk about algorithmic trading, we’re always talking about rules or setups to trigger the trade. The system can trade automatically or can offer signals for manual trades.

Python’s Role in Algorithmic Trading: Python is a popular choice for algo trading because it’s easy to use and has powerful libraries like Pandas and NumPy. These tools help with handling data and performing complex calculations, making it simpler to implement trading strategies. Also widely used for research and backtesting strategies.

Note that in real systems though, when we work with high-frequency trading, we usually use “more performative” low-level languages such as C++ to actually run the code, as the response time is important.

Let’s get into it with a simple workflow:

Workflow for Algo Trading:

  1. Define Objectives and Strategies:
    • Identify trading goals (e.g., maximize profits, minimize risks).
    • Develop trading strategies, such as moving averages, oscillators, etc.
    • Note that you need to define a hypothesis and check how it works with the market
  2. Data Collection:
    • Acquire relevant market data, such as asset prices, trading volumes, etc.
    • You can also use sentiment analysis and more complex info in your trades
  3. Data Preprocessing:
    • Clean and organize the data.
    • Calculate necessary technical indicators or statistics for the strategies.
  4. Algorithm Development:
    • Translate trading strategies into programmable algorithms.
    • Implement trading logic, risk management, and entry/exit conditions.
  5. Backtesting:
    • Test the algorithm on historical data to assess its past performance.
    • Identify potential improvements and adjustments.
  6. Optimization:
    • Refine and optimize the algorithm based on backtesting results.
  7. Implementation in Production Environment:
    • Deploy the algorithm in a production environment connected to the real-time market.
  8. Monitoring and Maintenance:
    • Monitor the algorithm’s performance in live market conditions.
    • Make adjustments as necessary.

As an example, let’s implement a simple Python program using yfinance to fetch Apple’s stock data and apply a moving average strategy.

Our hypothesis here is that if all the last 5 moving averages of a stock in the 5-day interval are greater than the 20-day interval moving average, it will grow even more so we should buy it and sell it after 7 days (arbitrary number).

Let’s follow some steps of the workflow, calculating for the last 100 days if we should buy or hold the stock at the given moment using the strategy

1. Define Objectives and Strategies:
We want to have profit — positive return — in our trade. Specifically a positive profit selling the stock one week after the buy.

2. Data Collection:

import yfinance as yf
import pandas as pd
symbol = 'AAPL'
start_date = pd.to_datetime('today') - pd.DateOffset(days=100)
end_date = pd.to_datetime('today')
stock_data = yf.download(symbol, start=start_date, end=end_date)

3. Data Preprocessing and 4. Algorithm Development:

def moving_average_strategy(data, short_window=5, long_window=20):
    # Calculate moving averages
    data['Short_MA'] = data['Close'].rolling(window=short_window, min_periods=1).mean()
    data['Long_MA'] = data['Close'].rolling(window=long_window, min_periods=1).mean()

    # Determine buy or hold signals
    signals = []
    for i in range(len(data)):
        if i >= short_window:
            if all(data['Short_MA'].iloc[i-short_window+1:i+1] > data['Long_MA'].iloc[i-short_window+1:i+1]):
                signals.append(('Buy', data.index[i]))
            else:
                signals.append(('Hold', data.index[i]))
        else:
            signals.append(('Hold', data.index[i]))

    data['Signal'] = signals
    return data

How to use it:

# calculating strategy data
strategy_data = moving_average_strategy(stock_data)

# display Signals for the Last 100 Days
last_100_days_signals = strategy_data.tail(100)['Signal']
for signal in last_100_days_signals:
    print(f"Date: {signal[1]}, Signal: {signal[0]}")

This ensures that a “Buy” signal is generated only when the short-term moving average is consistently greater than the long-term moving average over the last 5 days. Here’s part of the result we found:

Date: 2023-08-25 00:00:00, Signal: Hold
Date: 2023-08-28 00:00:00, Signal: Hold
Date: 2023-08-29 00:00:00, Signal: Hold
Date: 2023-08-30 00:00:00, Signal: Hold
Date: 2023-08-31 00:00:00, Signal: Hold
Date: 2023-09-01 00:00:00, Signal: Buy
Date: 2023-09-05 00:00:00, Signal: Buy
Date: 2023-09-06 00:00:00, Signal: Buy
Date: 2023-09-07 00:00:00, Signal: Buy
Date: 2023-09-08 00:00:00, Signal: Buy
Date: 2023-09-11 00:00:00, Signal: Buy
Date: 2023-09-12 00:00:00, Signal: Hold
Date: 2023-09-13 00:00:00, Signal: Hold
Date: 2023-09-14 00:00:00, Signal: Hold
Date: 2023-09-15 00:00:00, Signal: Hold
Date: 2023-09-18 00:00:00, Signal: Hold
Date: 2023-09-19 00:00:00, Signal: Hold
Date: 2023-09-20 00:00:00, Signal: Hold
Date: 2023-09-21 00:00:00, Signal: Hold
Date: 2023-09-22 00:00:00, Signal: Hold
Date: 2023-09-25 00:00:00, Signal: Hold
Date: 2023-09-26 00:00:00, Signal: Hold
[…]

So it shows us whether to buy or hold/keep the position based on our strategy — if a moving average of 5 days is consistently higher than a 20-day moving average for the last 5 days, we should buy the stock.

More than that, our hypothesis is that if we sell this stock 7 days later it will return a profit for us, so let’s test it:

5. (Back) testing it.

Here is a small test using the 100 days from our study to check how good we would be if we followed the strategy we implemented. Even so, I would not call this a proper backtesting as we usually do years of testing in the process. This small test we will reproduce is not conclusive at all, as we are limited to a small sample of data.

For doing this small test let’s write the following code, it will print for us the price of the stock when we bought it, when we sold it (7 days later), and the return

for signal in strategy_data.itertuples():
    if signal.Signal[0] == 'Buy':
        buy_date = signal.Signal[1]
        sell_date = buy_date + pd.DateOffset(weeks=1)

        # Ensure sell_date is within the data range
        if sell_date in strategy_data.index:
            buy_price = strategy_data.loc[buy_date, 'Close']
            sell_price = strategy_data.loc[sell_date, 'Close']

            # Calculate the return percentage
            return_percentage = ((sell_price - buy_price) / buy_price) * 100

            # Print the results
            print(f"Buy Date: {buy_date.strftime('%Y-%m-%d')}, Buy Price: {buy_price:.2f}, "
                  f"Sell Date: {sell_date.strftime('%Y-%m-%d')}, Sell Price: {sell_price:.2f}, "
                  f"Return Percentage: {return_percentage:.2f}%")

Here is the result of it:

Buy Date: 2023-09-01, Buy Price: 189.46, Sell Date: 2023-09-08, Sell Price: 178.18, Return Percentage: -5.95%
Buy Date: 2023-09-05, Buy Price: 189.70, Sell Date: 2023-09-12, Sell Price: 176.30, Return Percentage: -7.06%
Buy Date: 2023-09-06, Buy Price: 182.91, Sell Date: 2023-09-13, Sell Price: 174.21, Return Percentage: -4.76%
Buy Date: 2023-09-07, Buy Price: 177.56, Sell Date: 2023-09-14, Sell Price: 175.74, Return Percentage: -1.03%
Buy Date: 2023-09-08, Buy Price: 178.18, Sell Date: 2023-09-15, Sell Price: 175.01, Return Percentage: -1.78%
Buy Date: 2023-09-11, Buy Price: 179.36, Sell Date: 2023-09-18, Sell Price: 177.97, Return Percentage: -0.77%
Buy Date: 2023-10-13, Buy Price: 178.85, Sell Date: 2023-10-20, Sell Price: 172.88, Return Percentage: -3.34%
Buy Date: 2023-10-16, Buy Price: 178.72, Sell Date: 2023-10-23, Sell Price: 173.00, Return Percentage: -3.20%
Buy Date: 2023-10-17, Buy Price: 177.15, Sell Date: 2023-10-24, Sell Price: 173.44, Return Percentage: -2.09%
Buy Date: 2023-10-18, Buy Price: 175.84, Sell Date: 2023-10-25, Sell Price: 171.10, Return Percentage: -2.70%
Buy Date: 2023-10-19, Buy Price: 175.46, Sell Date: 2023-10-26, Sell Price: 166.89, Return Percentage: -4.88%
Buy Date: 2023-10-20, Buy Price: 172.88, Sell Date: 2023-10-27, Sell Price: 168.22, Return Percentage: -2.70%

Wow! Our strategy is really bad 😅.

Actually, it might be a good idea to short the stock if you find this pattern of moving averages, as we were able to consistently check a lower price in the following 7 days!

Again, more backtesting is needed — We usually check years of data and verify how our strategy performed during this time to make conclusions and talk about results, then we can talk about optimizations in the model and whatnot.

Just to follow our example, one good 6. Optimization would also incorporate other variables such as relative strength index (RSI) above a certain level to trigger the buy action. That would make the strategy more adaptable and robust for the real market, for instance (again, more backtesting is needed so you can check these patterns).

Finally, for the 7th and 8th steps — implement in production and monitor results you can incorporate APIs from your favorite trading platform into your Python script and use it. This same program can keep note of your orders and calculate returns etc, so you can keep track of it and optimize as needed.

That is it for this post! Hope it sheds some light on handling your orders and creating your finance programs. If you’ve got more questions or need a hand, give me a shout in the comments.

Happy coding and good luck with your trades!

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top