How to Build a Quantitative Trading System in Python: A Practical Guide to abupy

7 views 0 likes 0 comments 21 minutesOriginalTutorial

Step-by-step guide to setting up a Python quantitative trading environment, fetching market data, coding a Moving Average Crossover strategy, and executing a full backtest using the open-source framework abupy.

#Python Quantitative Trading # abupy # Quant Backtesting # Algorithmic Stock Strategy # Machine Learning Trading
How to Build a Quantitative Trading System in Python: A Practical Guide to abupy

How to Build a Quantitative Trading System in Python: A Practical Guide to abupy

Introduction

Have you ever been in this awkward situation: a technical indicator like "golden cross buy, death cross sell" sounds logical, but you hesitate to use real money to test it? Or maybe you wrote dozens of lines of backtesting code only to realize you forgot to account for transaction fees and slippage, rendering the profit numbers completely misleading?

Quantitative trading sounds like a high barrier to entry, but the core logic is actually just three steps: get data → define rules → verify results. Today, I'll walk you through using the open-source quantitative framework abupy (18K+ stars) to complete this entire loop in 30 minutes and run your first truly meaningful strategy backtest.

Prerequisites

Before starting, please ensure you meet the following requirements:

  • Python 3.7+ installed (strongly recommend using Anaconda, as it pre-compiles most quantitative dependencies).
  • Basic understanding of Python syntax (variables, lists, functions, if/for).
  • Basic knowledge of the stock market (it's enough to know what K-lines, moving averages, and closing prices are).

Prior experience with quantitative trading is not required. Just follow along; abupy is very beginner-friendly.

Environment Setup

Why recommend Anaconda? Quantitative projects rely heavily on libraries like numpy, pandas, and scipy. These often have C extensions and version conflicts. Conda's binary pre-compilation can save you from 90% of environment headaches.

Open your terminal (Anaconda Prompt on Windows, standard terminal on Mac/Linux):

bash 复制代码
## 1. Create a dedicated quant environment (to avoid polluting global Python)
conda create -n quant python=3.8
conda activate quant

## 2. Install abupy
pip install abupy

## 3. Verify installation
python -c "import abupy; print('abupy installed successfully')"

If it prints successfully, the environment is ready. On its first run, abupy will automatically initialize a local cache directory (usually ~/.abupy/), so wait a few seconds for the data directories to be created.

Step 1: Fetching Historical Market Data

The first step in quantitative trading is always getting clean data. abupy has built-in multiple data sources, fetching data for A-shares, US stocks, HK stocks, futures, and crypto is just a command away.

Why not crawl data yourself? Cleaning historical data is a trap: dealing with dividends, stock splits, suspensions, and aligning missing data to trading days... abupy has already done all this dirty work for you; just call it to get a clean DataFrame.

python 复制代码
from abupy import ABuSymbolPd

## Fetch daily K-line data for the past year for Tencent Holdings (HK Stock)
symbol = 'hk00700'
kl_data = ABuSymbolPd.make_kl_df(symbol, n_folds=1)

if kl_data is not None:
    print(f'Successfully fetched {len(kl_data)} data rows')
    print(kl_data.tail())

make_kl_df is the core data interface in abupy. n_folds=1 represents 1 year of data, which can be changed to 2, 3, 5 as needed. It returns a pandas DataFrame with a time index, where each row contains fields like open/close/high/low/volume—this is the raw material for all your subsequent analysis.

Step 2: Coding Your First Timing Strategy

Looking at data alone is meaningless; we want to verify if a strategy can actually make money.

Today we will build the classic Dual Moving Average Strategy: Buy when the 10-day moving average crosses above the 30-day moving average (Golden Cross), and sell when it drops below (Death Cross).

Why choose the MA strategy? The logic is clear, parameters are intuitive, and it's easy to understand why it fails. Once you are familiar with the backtesting framework, moving to more complex factors becomes natural.

In abupy, strategies exist as Factors. Buy factors inherit from AbuFactorBuyXD, and sell factors from AbuFactorSellXD. The framework automatically calls the fit_day method on every trading day to check if signals are triggered.

python 复制代码
from abupy import AbuFactorBuyXD, AbuFactorSellXD
import numpy as np

class MaCrossBuy(AbuFactorBuyXD):
    """Buy when 10-day MA crosses up 30-day MA"""
    def fit_day(self, today, orders):
        short = self.kl_pd.close_price.rolling(10).mean().dropna()
        long = self.kl_pd.close_price.rolling(30).mean().dropna()
        # Get intersection to calculate golden cross
        common_idx = short.index.intersection(long.index)
        if len(common_idx) < 2:
            return False
        s_cur, s_pre = short.loc[common_idx[-1]], short.loc[common_idx[-2]]
        l_cur, l_pre = long.loc[common_idx[-1]], long.loc[common_idx[-2]]
        # Yesterday short MA was below, today above -> Golden Cross
        if s_pre <= l_pre and s_cur > l_cur:
            self.buy_today = True
            return True
        return False

class MaCrossSell(AbuFactorSellXD):
    """Sell when 10-day MA crosses down 30-day MA"""
    def fit_day(self, today, orders, pick_times):
        short = self.kl_pd.close_price.rolling(10).mean().dropna()
        long = self.kl_pd.close_price.rolling(30).mean().dropna()
        common_idx = short.index.intersection(long.index)
        if len(common_idx) < 2:
            return False
        s_cur, s_pre = short.loc[common_idx[-1]], short.loc[common_idx[-2]]
        l_cur, l_pre = long.loc[common_idx[-1]], long.loc[common_idx[-2]]
        if s_pre >= l_pre and s_cur < l_cur:
            return True
        return False

Key Notes:

  • self.kl_pd inside fit_day is the full K-line data for the current target, and today is the current trading day info.
  • The golden cross judgment uses the relationship between yesterday and today to avoid using future data.
  • Returning True triggers the signal, and the framework records the order automatically.

Step 3: Configuring and Running the Backtest

With the factors written, next we tell abupy what targets to use, how much capital we have, and the backtest time range.

python 复制代码
from abupy import AbuPickTimeWorker, AbuBenchmark, AbuCapital, AbuMetricsBase

## Set benchmark (using Shanghai Composite Index for comparison)
benchmark = AbuBenchmark(benchmark='sz000001')

## Initial capital 1 million
capital = AbuCapital(1000000, benchmark)

## Assemble factors
buy_factors = [{'class': MaCrossBuy}]
sell_factors = [{'class': MaCrossSell}]

## Execute backtest
worker = AbuPickTimeWorker(
    capital=capital,
    benchmark=benchmark,
    buy_factors=buy_factors,
    sell_factors=sell_factors,
    choice_symbols=['hk00700']
)
worker.fit()

## Calculate metrics
metrics = AbuMetricsBase(worker.orders, worker.action_flags, benchmark, capital)
metrics.fit_metrics()
metrics.plot_returns_cmp(only_show_returns=True)

After this code runs, you will see a comparison chart: Strategy Equity vs Benchmark line. metrics.fit_metrics() automatically calculates core values like Annualized Return, Max Drawdown, and Sharpe Ratio.

Meaning of key parameters:

  • AbuCapital(1000000, benchmark): Sets the initial principal; abupy uses this to calculate position sizes.
  • choice_symbols: You can pass multiple targets, and the framework will backtest them in parallel.
  • plot_returns_cmp(): Visual comparison, letting you see at a glance if the strategy beats the market.

Step 4: Understanding Backtesting Metrics

Running the backtest is not the end, but the beginning. You need to judge if the strategy is reliable.

  • Annualized Return: The average return converted to a yearly figure. This number alone is useless; it must be compared against the market during the same period.
  • Max Drawdown: The largest percentage drop from a peak to a trough during the strategy's period. This represents the most pain you might feel psychologically and determines if you can hold the position.
  • Sharpe Ratio: The ratio of return to risk. Below 1 implies poor cost-performance; above 1.5 is considered excellent.
  • Win Rate: The percentage of profitable trades. However, a high win rate doesn't equal profitability; the profit-to-loss ratio matters more.

Beware of Overfitting: If a set of parameters yields very high backtest returns but crashes with another stock or time period, the strategy just overfitted the historical data and lacks generalization. A truly good strategy should perform stably across multiple targets and timeframes.

Common Issues and Pitfalls

Stuck or Slow Download on First Run

abupy creates cache directories and downloads historical data during initialization, which may take a few minutes. If the network is slow, you can set a proxy. Be patient and wait for the progress to complete; do not force interrupt.

Results Too Good to Be True

A common mistake for beginners: Look-ahead Bias. For example, using the closing price of the day for a buy signal, but in live trading, you don't have the closing price at the open. abupy's fit_day mechanism is designed to avoid this, but you must ensure custom factors strictly use past data only.

Missing Fees and Slippage

Default backtests might not include transaction costs, which artificially inflates returns. Before actual deployment, ensure you enable commissions (typically 0.03% plus stamp duty for A-shares) and slippage simulation in the configuration.

Conclusion and Next Steps

Today we went through the complete quantitative trading loop:

  1. Set up Python + abupy environment
  2. Pulled historical K-lines with make_kl_df
  3. Wrote Moving Average Crossover factors and ran the backtest
  4. Read core metrics to evaluate strategy validity

Next steps I suggest:

  • Validate with other targets: Switch from HK stocks to A-shares (e.g., sz000001) or US stocks (usTSLA) to see stability across markets.
  • Add filters: Overlay volume thresholds on the buy signal, e.g., only enter on breakouts with high volume.
  • Read official tutorials: The abupy_lecture directory in the repo contains full Jupyter Notebooks covering stop-loss, position management, and machine learning stock selection. Going through them will level you up.

Quantitative trading isn't about writing magic code to guarantee profits; its true value lies in making your trading decisions data-driven and verifiable. Get the process running with abupy first, then slowly polish your strategy—that's the right way to do it.

Last Updated:

Comments (0)

Post Comment

Loading...
0/500

No comments yet, be the first to comment!