Skip to main content

A trading backtesting framework for Python

Project description

Trading-Backtester

A Python library for backtesting trading strategies. It allows users to simulate historical trades, evaluate strategy performance, and analyze key metrics such as returns, drawdowns, and risk. The library supports custom indicators, making it suitable for both simple and advanced strategies.

Requirements

  • numpy - for data and numeric manipulation
  • matplotlib - for plotting results

Why numpy?

Of course I could handle most mathematical operation using regular floats number, but NumPy is widely used for such tasks and help make code more structured. What's more, NumPy is supported by many trading indicators libraries - for example, I wanted to use my backtesting engine with TA-Lib, library that takes NumPy arrays as input.

Why another "Backtesting" library

There are many backtesting libraries and platforms available - that's right. However, when I was looking for Python backtesting library that could handle some specific requirements for my strategies - such as placing different orders at market open and closes, detecting price gaps, etc. - I found that the features set of popular libraries were not sufficient.

Additionally, building my own backtesting engine served as a great side project for learing and improving my development skills.

Installation

Library is available on PyPI.

python3 -m pip install trading-backtester

Usage example

Declare RSI Indicator

We use TA-Lib to calculate Relative Strength Indicator values.

class RsiIndicator(Indicator):
    def __init__(self, period: int):
        super().__init__()
        self.__period = period

    def _calc_indicator_values(self, data: Data) -> np.ndarray[Any, np.dtype[Any]]:
        rsi = talib.RSI(data.close, timeperiod=self.__period)
        return rsi

Declare trading strategy

Our example trading strategy is based on the RSI Indicator:

  • When RSI Indicator value crosses above 30 from below:
    • Open a long position
    • Close any existing short position
  • When RSI indicator values crosses below 70 from above:
    • Open short position
    • Close any existing long position
class RsiStrategy(Strategy):
    def __init__(
        self,
    ):
        super().__init__()
        self.__rsi_indicator = RsiIndicator(period=14)

    def collect_orders(
        self, candlestick_phase: CandlestickPhase, price: float, date_time: datetime
    ) -> List[Order]:
        orders: List[Order] = []

        if self.__rsi_indicator[-1] < 30 and self.__rsi_indicator[0] >= 30:
            # Open a long position when RSI crosses above 30
            # and close short position if exists

            if (
                len(self._positions) != 0
                and self._positions[0].position_type == PositionType.SHORT
            ):
                # Close short position if exists
                orders.append(
                    CloseOrder(
                        size=self._positions[0].size,
                        position_type=PositionType.SHORT,
                    )
                )

            if (
                len(self._positions) == 0
                or self._positions[0].position_type != PositionType.LONG
            ):
                orders.append(
                    OpenOrder(
                        size=1,
                        position_type=PositionType.LONG,
                    )
                )

        elif self.__rsi_indicator[-1] > 70 and self.__rsi_indicator[0] <= 70:
            # Open a short position when RSI crosses below 70
            # and close long position if exists

            if (
                len(self._positions) != 0
                and self._positions[0].position_type == PositionType.LONG
            ):
                # Close long position if exists
                orders.append(
                    CloseOrder(
                        size=self._positions[0].size,
                        position_type=PositionType.LONG,
                    )
                )

            if (
                len(self._positions) == 0
                or self._positions[0].position_type != PositionType.SHORT
            ):
                orders.append(
                    OpenOrder(
                        size=1,
                        position_type=PositionType.SHORT,
                    )
                )

        return orders

Declare Backtester

For example purposes, we assume 1% broker's commission and no spread.

commission = Commission(CommissionType.RELATIVE, 0.001)
backtest = Backtester(
    data, RsiStrategy, money=10000, benchmark=data, commission=commission
)
backtest.run()

Print statistics and draw plot

stats = backtest.get_statistics()
print(stats)

plotting = backtest.get_plotting()
plotting.show_plot()
=== Statistics ===
Total trades: 41
Total open trades: 21
Total close trades: 20
Total open long trades: 11
Total close long trades: 10
Total open short trades: 10
Total close short trades: 10
Final money: 5069.67
Final total equity: 10638.73
Return: 638.73 (6.39%)
Max drawdown: 2012.18 (17.66%)
Max drawdown duration: 763
Winning trades: 15 (75.00%)
Best trade return: 20.38%
Worst trade return: -34.94%
Beta: 0.10
Alpha: -0.11
Buy and hold return: 166.80%
Total commission paid: 134.66

RSI Indicator example result Plot

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

trading_backtester-1.0.0.tar.gz (23.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

trading_backtester-1.0.0-py3-none-any.whl (27.3 kB view details)

Uploaded Python 3

File details

Details for the file trading_backtester-1.0.0.tar.gz.

File metadata

  • Download URL: trading_backtester-1.0.0.tar.gz
  • Upload date:
  • Size: 23.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.3

File hashes

Hashes for trading_backtester-1.0.0.tar.gz
Algorithm Hash digest
SHA256 0df3d7421814751187bdb30a018a54fc75bbe341957fb3d5e8ec99b5d6faf1b8
MD5 e8260f53aed311e32aff0ec1ac67c2a0
BLAKE2b-256 63537941d231dae91f679e5888781fe23f39373d8363a93681129a64673ebb28

See more details on using hashes here.

File details

Details for the file trading_backtester-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for trading_backtester-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3a030c99800feea6048349a88c63f4cdb9ef5c5a7901919db017dc4ef12fdd76
MD5 1efabca092f88af1f4b7014c96cc1d7d
BLAKE2b-256 4565feb5f0f03a6c15f3bd90c4bd0ced56da08fb1353f6cdaba8065ab9f994d5

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page