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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0df3d7421814751187bdb30a018a54fc75bbe341957fb3d5e8ec99b5d6faf1b8
|
|
| MD5 |
e8260f53aed311e32aff0ec1ac67c2a0
|
|
| BLAKE2b-256 |
63537941d231dae91f679e5888781fe23f39373d8363a93681129a64673ebb28
|
File details
Details for the file trading_backtester-1.0.0-py3-none-any.whl.
File metadata
- Download URL: trading_backtester-1.0.0-py3-none-any.whl
- Upload date:
- Size: 27.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3a030c99800feea6048349a88c63f4cdb9ef5c5a7901919db017dc4ef12fdd76
|
|
| MD5 |
1efabca092f88af1f4b7014c96cc1d7d
|
|
| BLAKE2b-256 |
4565feb5f0f03a6c15f3bd90c4bd0ced56da08fb1353f6cdaba8065ab9f994d5
|