Epymetheus is a Python library for multi-asset backtesting. It provides an end-to-end framework that lets analysts build and try out their trade strategy right away.
- Simple and Intuitive API: The API is minimally organized so that you can focus on your idea. Trade
Strategy
can be readily coded and its backtesting is consistently carried out by its methodsrun()
andevaluate()
. - Seamless connection to Pandas: You can just put in pandas DataFrame as an input historical data. Backtesting results can be quickly converted to Pandas format so that you can view, analyze and plot results by the familiar Pandas methods.
- Extensibility with Other Frameworks: Epymetheus only provides a framework. Strategy can be readily built with other libraries for machine learning, econometrics, technical indicators, derivative pricing models and so forth.
- Efficient Computation: Backtesting engine is boosted by NumPy. You can give your own idea a quick try.
- Full Test Coverage: Epymetheus is thoroughly tested with 100% test coverage for multiple Python versions.
- Strategy: A strategy encodes your own trading rules. The
benchmarks
provide standard strategies to be compared with. - Universe: A universe stores historical prices of a set of securities. The
datasets
provide sample universe like Brownian stock prices and blue chips in the US. - History: A history stores the assets, lots, profit/loss of each trade yielded. Easily converted into Pandas DataFrame.
- Metric: A metric is a function to assess the performance of your strategy. Available metrics include: final wealth, maximum drawdown, Sharpe ratio and so forth.
Strategies may be integrated with:
- Machine Learning: scikit-learn, TensorFlow, PyTorch, etc.
- Econometrics: statsmodels, etc.
- Technical Indicators: TA-Lib, etc.
- Derivative Pricing: TF Quant Finance, etc.
Example codes are provided here.
$ pip install epymetheus
Let's construct your own Strategy
.
from epymetheus import Trade, Strategy
class MyStrategy(Strategy):
"""
This is my favorite strategy.
Parameters
----------
- my_parameter : float
My awesome parameter.
"""
def __init__(self, my_parameter):
self.my_parameter = my_parameter
def logic(self, universe):
...
yield Trade(...)
strategy = MyStrategy(my_parameter=0.1)
Your strategy can readily be backtested with any Universe
.
from epymetheus.datasets import fetch_usstocks
universe = Universe(df_prices) # The historical prices that you have, or
universe = fetch_usstocks(n_assets=10) # Basic datasets provided by Epymetheus.
strategy.run(universe)
# Running ...
# Generating 478 trades (2019-12-31) ... Done. (Runtime : 0.30 sec)
# Executing 478 trades ... Done. (Runtime : 0.41 sec)
# Done. (Runtime : 0.71 sec)
Now the results can be accessed as the attributes of strategy
.
You can plot the wealth right away:
df_wealth = strategy.wealth.to_dataframe()
df_wealth.plot()
You can also quickly evaluate
various metrics of the perfornance.
For example, drawdown, exposure and Sharpe ratio are given by:
from epymetheus.metrics import Drawdown, MaxDrawdown, Exposure, SharpeRatio
drawdown = strategy.evaluate(Drawdown())
max_drawdown = strategy.evaluate(MaxDrawdown())
net_exposure = strategy.evaluate(Exposure(net=True))
abs_exposure = strategy.evaluate(Exposure(net=False))
sharpe_ratio = strategy.evaluate(SharpeRatio())
Detailed trade history can be viewed as:
strategy.history.to_dataframe()
# or: pandas.DataFrame(strategy.history)
order_id | trade_id | asset | lot | open_bar | close_bar | shut_bar | take | stop | pnl |
---|---|---|---|---|---|---|---|---|---|
0 | 0 | AMZN | 145.191 | 2000-02-29 | 2000-04-05 | 2000-05-29 | 1000 | -1000 | -970.962 |
1 | 0 | BRK-A | -0.0227273 | 2000-02-29 | 2000-04-05 | 2000-05-29 | 1000 | -1000 | -288.636 |
2 | 1 | JPM | 346.101 | 2000-02-29 | 2000-03-16 | 2000-05-29 | 1000 | -1000 | 1318.68 |
3 | 1 | WMT | -29.9542 | 2000-02-29 | 2000-03-16 | 2000-05-29 | 1000 | -1000 | -121.923 |
4 | 2 | BRK-A | 0.174825 | 2000-03-31 | 2000-06-30 | 2000-06-30 | 1000 | -1000 | -594.406 |
Profit-loss distribution can be accessed by:
pnl = strategy.history.groupby('trade_id').aggregate(sum).pnl
plt.hist(pnl)