Giter VIP home page Giter VIP logo

ssantoshp / empyrial Goto Github PK

View Code? Open in Web Editor NEW
864.0 30.0 119.0 93.19 MB

An Open Source Portfolio Backtesting Engine for Everyone | 面向所有人的开源投资组合回测引擎

Home Page: https://empyrial.gitbook.io/empyrial/

License: MIT License

Python 100.00%
quantitative-finance quantitative-analysis stock stock-market backtesting finance portfolio-analysis investment-portfolio portfolio-optimization portfolio-management

empyrial's Introduction

By Investors, For Investors.











Open In Colab


Want to read this in Chinese? Click here

Empyrial is a Python-based open-source quantitative investment library dedicated to financial institutions and retail investors, officially released in March 2021. Already used by thousands of people working in the finance industry, Empyrial aims to become an all-in-one platform for portfolio management, analysis, and optimization.

Empyrial empowers portfolio management by bringing the best of performance and risk analysis in an easy-to-understand, flexible and powerful framework.

With Empyrial, you can easily analyze security or a portfolio in order to get the best insights from it. This is mainly a wrapper of financial analysis libraries such as Quantstats and PyPortfolioOpt.



Installation

You can install Empyrial using pip:

pip install empyrial

For a better experience, we advise you to use Empyrial on a notebook (e.g., Jupyter, Google Colab)

Note: macOS users will need to install Xcode Command Line Tools.

Note: Windows users will need to install C++. (download, install instructions)

Features

Feature 📰 Status
Engine (backtesting + performance analysis) Released on May 30, 2021
Optimizer Released on Jun 7, 2021
Rebalancing Released on Jun 27, 2021
Risk manager Released on Jul 5, 2021
Sandbox Released on Jul 17, 2021
Support for custom data Released on Aug 12, 2023

Documentation

Full documentation (website)

Usage

Empyrial Engine

from empyrial import empyrial, Engine

portfolio = Engine(
    start_date = "2018-08-01", 
    portfolio = ["BABA", "PDD", "KO", "AMD","^IXIC"], 
    weights = [0.2, 0.2, 0.2, 0.2, 0.2],  # equal weighting is set by default
    benchmark = ["SPY"]  # SPY is set by default
)

empyrial(portfolio)

Use custom data

See doc here to learn how to do this.

Calendar Rebalancing

A portfolio can be rebalanced for either a specific time period or for specific dates using the rebalance option.

Rebalance for Time Period

Time periods available for rebalancing are 2y, 1y, 6mo, quarterly, monthly

from empyrial import empyrial, Engine

portfolio = Engine(
    start_date = "2018-08-01", 
    portfolio = ["BABA", "PDD", "KO", "AMD","^IXIC"], 
    weights = [0.2, 0.2, 0.2, 0.2, 0.2],  # equal weighting is set by default
    benchmark = ["SPY"],  # SPY is set by default
    rebalance = "1y"
)

empyrial(portfolio)

Rebalance for Custom Dates

You can rebalance a portfolio by specifying a list of custom dates.
⚠️ When using custom dates, the first date of the list must correspond with the start_date and the last element should correspond to the end_date which is today's date by default.

from empyrial import empyrial, Engine

portfolio = Engine(
    start_date = "2018-08-01", 
    portfolio = ["BABA", "PDD", "KO", "AMD","^IXIC"], 
    weights = [0.2, 0.2, 0.2, 0.2, 0.2],  # equal weighting is set by default
    benchmark = ["SPY"],  # SPY is set by default
    rebalance = ["2018-06-09", "2019-01-01", "2020-01-01", "2021-01-01"]
)

empyrial(portfolio)

Optimizer

The default optimizer is equal weighting. You can specify custom weights, if desired.

from empyrial import empyrial, Engine

portfolio = Engine(
    start_date = "2018-08-01",
    portfolio = ["BABA", "PDD", "KO", "AMD","^IXIC"], 
    weights = [0.1, 0.3, 0.15, 0.25, 0.2],   # custom weights
    rebalance = "1y"  # rebalance every year
)

empyrial(portfolio)

You can also use the built-in optimizers. There are 4 optimizers available:

  • "EF": Global Efficient Frontier Example
  • "MEANVAR": Mean-Variance Example
  • "HRP": Hierarchical Risk Parity Example
  • "MINVAR": Minimum-Variance Example
from empyrial import empyrial, Engine

portfolio = Engine(
    start_date = "2018-08-01",
    portfolio = ["BABA", "PDD", "KO", "AMD","^IXIC"],
    optimizer = "EF",
    rebalance = "1y"  # rebalance every year
)

portfolio.weights

Output:

[0.0, 0.0, 0.0348, 0.9652, 0.0]

We can see that the allocation has been optimized.

Risk Manager

3 Risk Managers are available:

  • Max Drawdown: {"Max Drawdown" : -0.3} Example
  • Take Profit: {"Take Profit" : 0.4} Example
  • Stop Loss: {"Stop Loss" : -0.2} Example
from empyrial import empyrial, Engine

portfolio = Engine(
    start_date = "2018-08-01",
    portfolio= ["BABA", "PDD", "KO", "AMD","^IXIC"], 
    optimizer = "EF",
    rebalance = "1y",  # rebalance every year
    risk_manager = {"Max Drawdown" : -0.2}  # Stop the investment when the drawdown becomes superior to -20%
)

empyrial(portfolio)

Empyrial Outputs

image image image image image image image image image image image

Download the Tearsheet

You can use the get_report() function of Empyrial to generate a tearsheet, and then download this as a PDF document.

from empyrial import get_report, Engine

portfolio = Engine(
      start_date = "2018-08-01",
      portfolio = ["BABA", "PDD", "KO", "AMD","^IXIC"],
      optimizer = "EF",
      rebalance = "1y", #rebalance every year
      risk_manager = {"Stop Loss" : -0.2}
)

get_report(portfolio)

Output:

image

Stargazers over time

追星族的时间

Contribution and Issues

Empyrial uses GitHub to host its source code. Learn more about the Github flow.

For larger changes (e.g., new feature request, large refactoring), please open an issue to discuss first.

Smaller improvements (e.g., document improvements, bugfixes) can be handled by the Pull Request process of GitHub: pull requests.

  • To contribute to the code, you will need to do the following:

  • Fork Empyrial - Click the Fork button at the upper right corner of this page.

  • Clone your own fork. E.g., git clone https://github.com/ssantoshp/Empyrial.git
    If your fork is out of date, then will you need to manually sync your fork: Synchronization method

  • Create a Pull Request using your fork as the compare head repository.

You contributions will be reviewed, potentially modified, and hopefully merged into Empyrial.

Contributors

Thanks goes to these wonderful people (emoji key):

All Contributors


Brendan Glancy

💻 🐛

Renan Lopes

💻 🐛

Mark Thebault

💻

Diego Alvarez

💻🐛

Rakesh Bhat

💻

Anh Le

🐛

Tony Zhang

💻

Ikko Ashimine

✒️

QuantNomad

📹

Buckley

✒️💻

Adam Nelsson

💻

Ranjan Grover

🐛💻

This project follows the all-contributors specification. Contributions of any kind are welcome!

Credit

This library has also been made possible because of the work of these incredible people:

Contact

You are welcome to contact us by email at [email protected] or in Empyrial's discussion space

License

MIT

empyrial's People

Contributors

abhinavslohiya avatar brendanglancy avatar buckleyc avatar diegodalvarez avatar dowlatabadi avatar eltociear avatar haizzz avatar markthebault avatar pzzhang avatar rakeshbhat9 avatar rgleavenworth avatar rslopes avatar ssantoshp avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

empyrial's Issues

No branch for the src of the website

The website branch is just the build files, there is no way to edit the content of the website like this
is it possible to add another branch so the src for the website can be edited, if you do I still have the source code on my computer and I will push it asap

Can this framework set ime-varying weights for assets in the portfolio?

Hi,

According to the documentm, seems when backtesting, the weights of assets are fixed. Can the framework allow time-varying weights? For example, we can input different weights for each time point, in this case, we can combine the stocks selection strategy (some stocks into the portfolio, some stocks out of the portfolio) with the portfolio performance test. If you can set up this, that would be very helpful.

Pickle cache for candles

When doing multiple tests it might speed things up to cache the candles for example with pickle to prevent redownloading them again.
This might speed everything up a little.

Nevertheless thank you for this great package!

RemoteDataError: No data fetched using 'YahooDailyReader'

Discussed in https://github.com/ssantoshp/Empyrial/discussions/27

Originally posted by karim1104 July 3, 2021
Starting July 1, I'm getting the error "RemoteDataError: No data fetched using 'YahooDailyReader'". I've tried it in different Python environments (3.6, 3.8, 3.9).
It seems like a Pandas DataReader issue (pydata/pandas-datareader#868)
How can we resolve this? I have a subscription to FMP, is there a way I use instead of Yahoo Finance?

assets value / non-stock based portfolio?

Wondering if Empyrial can be used with a non-stock based portfolio. The example in the docs is like this:

from empyrial import empyrial, Engine
portfolio = Engine(
start_date= "2018-06-09",
portfolio= ["BABA", "PDD", "KO", "AMD","^IXIC"],
weights = [0.2, 0.2, 0.2, 0.2, 0.2], #equal weighting is set by default
benchmark = ["SPY"] #SPY is set by default
)
empyrial(portfolio)

Is there any alternate way to define a portfolio, not as a list of stocks / weights but based on the value of the assets in the account?

Annual return in bar diagram or similar

First of all your project is Awesome !!!!

Is your feature request related to a problem? Please describe.
No

Describe the solution you'd like
I would like to see easily for each year what is my % return, Ideally, it will be a bar chart.

Describe alternatives you've considered
In the monthly return add a new column for the annual return

Additional context

Screenshot 2021-08-03 at 12 57 20

Hard-coded Risk-free rate

The risk-free rate is hard-coded in empyrial.py in line 197 (rf = 0.0).
Please change it to a variable so we can adjust it as needed.

Report Rename

It would be awesome if we can rename the reports or it is renamed automatically.

Test example is broken

Describe the bug
Empyrial/test/rebalance_test.py and Empyrial/test/optimizer_test.py doesn't run.

To Reproduce

(base) pdm run python Empyrial/test/rebalance_test.py 
Traceback (most recent call last):
  File "Empyrial/test/rebalance_test.py", line 14, in <module>
    empyrial(portfolio, rebalance = True)
TypeError: empyrial() got an unexpected keyword argument 'rebalance'
(base) pdm run python Empyrial/test/optimizer_test.py 
Traceback (most recent call last):
  File "Empyrial/test/optimizer_test.py", line 9, in <module>
    optimizer(portfolio, "EF")
NameError: name 'optimizer' is not defined

I can't find the function optimizer in the code, and the parameter rebalance in the header of function empyrial

Rolling Beta to Benchmark

Hi Santosh,

Looking at the generated report, I noticed that the "Rolling Beta to Benchmark" always starts with 0.
If we're looking at 6-month basis, for the first 6 months, the line is at 0, after that the line starts to change.
If we're looking at 12-month basis, for the first 12 months, the line is at 0, after that the line starts to change.
Is this a bug or by design? If by design, what's the logic behind it?
Thanks!

rebalance has a bug

When you set up quarterly rebalance with only one ticker, the strategy and benchmark show different values. This is a bug. They should be completely equal.

The codebase below reproduces the issue. The EOY returns and the timeseries plot of Cumulative returns vs benchmark show that the strategy and benchmark are divergent.

from empyrial import empyrial, Engine
portfolio = Engine(
start_date= "2021-01-01",
portfolio= ["BTC-USD", "GOOG"],
weights = [1, 0.], #equal weighting is set by default
benchmark = ["BTC-USD"], #SPY is set by default
rebalance = 'quarterly'
)
empyrial(portfolio)

Bloomberg

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when X happens [...]

Describe the solution you'd like
A clear and concise description of what you want to happen.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

str and Timestamp error

The code:

from empyrial import empyrial, Engine
portfolio = Engine(
                  start_date= "2021-01-01", #start date for the backtesting
                  end_date= "2022-05-01",
                  portfolio= tickers[:], #assets in your portfolio
                  weights = w2[:],
                  benchmark=["XU100.IS"]
)
print(empyrial(portfolio))
print(portfolio)

It gives an error like below.

TypeError Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_10148/966461475.py in
11 )
12
---> 13 print(empyrial(portfolio))
14 print(portfolio)

~\AppData\Roaming\Python\Python39\site-packages\empyrial.py in empyrial(my_portfolio, rf, sigma_value, confidence_value)
304 empyrial.SR = SR
305
--> 306 CR = qs.stats.calmar(returns)
307 CR = CR.tolist()
308 CR = str(round(CR, 2))

~\AppData\Roaming\Python\Python39\site-packages\quantstats\stats.py in calmar(returns, prepare_returns)
547 if prepare_returns:
548 returns = _utils._prepare_returns(returns)
--> 549 cagr_ratio = cagr(returns)
550 max_dd = max_drawdown(returns)
551 return cagr_ratio / abs(max_dd)

~\AppData\Roaming\Python\Python39\site-packages\quantstats\stats.py in cagr(returns, rf, compounded)
500 total = _np.sum(total)
501
--> 502 years = (returns.index[-1] - returns.index[0]).days / 365.
503
504 res = abs(total + 1.0) ** (1.0 / years) - 1

TypeError: unsupported operand type(s) for -: 'str' and 'Timestamp'

EM Optimizer fails if benchmark changed to Nifty50 (yahoo ticker used "^NSEI")

Describe the bug
The EM optimiser fails when the default benchmark is altered to Nifty .

However if the default is restored it works .

To Reproduce
Steps to reproduce the behavior :
use this code
"from empyrial import empyrial, Engine

portfolio = Engine(
start_date= "2015-01-01", #start date for the backtesting
portfolio= ["TCS.NS", "INFY.NS", "HDFC.NS", "KOTAKBANK.NS","TITAN.NS","NESTLEIND.NS"], #assets in your portfolio
benchmark = ["NSEI"]
optimizer = "EF"
)
empyrial(portfolio)"

Expected behavior
error message
" File "/var/folders/41/q1hx0rjd5xzck1vl121t6b2m0000gn/T/ipykernel_2924/1251204071.py", line 7
optimizer = "EF"
^
SyntaxError: invalid syntax

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: MacOsx
  • Browser Chrome
  • jupyter

Additional context
Add any other context about the problem here.

Error when rebalancing with only one stock

Hi,
I have tried to reproduce test results and simulated one single stock over time by forcing the weight distribution as shown below:
tickers = ["stock1", "stock2"]
weights_new_ = [1.0, 0.0]
no optimizer is used, so just using the quantstats calculations of ratios and returns.
In the next example, we do the same but with a yearly rebalancer. The thing here is that the results should be exactly the same.
There seems to be a slight error in the returns calculations over time, which turns out to be bigger with more rebalancing.

I will have some more look at it, and update if I find the bug. Btw great work!

volatility() got an unexpected keyword argument 'trading_year_days'

Describe the bug
A clear and concise description of what the bug is.

To Reproduce
Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: [e.g. iOS]
  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Additional context
Add any other context about the problem here.

Error when running fundlens

Anaconda3\lib\site-packages\empyrial.py", line 610, in fundlens
['Dividend yield', yahoo_financials.get_dividend_yield()], ['Payout ratio', yahoo_financials.get_payout_ratio()], ['Controversy', controversy], ['Social score', social_score],

UnboundLocalError: local variable 'controversy' referenced before assignment

TypeError: float() argument must be a string or a number, not 'DataFrame'

When I run a test code:

from empyrial import empyrial, Engine

portfolio = Engine(
start_date = "2018-08-01",
portfolio = ["BABA", "PDD", "KO", "AMD","^IXIC"],
weights = [0.2, 0.2, 0.2, 0.2, 0.2], # equal weighting is set by default
benchmark = ["SPY"] # SPY is set by default
)

empyrial(portfolio)

give me a following error:

TypeError: float() argument must be a string or a number, not 'DataFrame'

Captura de pantalla 2024-04-13 a la(s) 1 14 35 p  m

Could anyone to help me with this issue?

Thanks

Failed to build scs ERROR: Could not build wheels for scs which use PEP 517 and cannot be installed directly

Describe the bug
A clear and concise description of what the bug is.

To Reproduce
Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: [e.g. iOS]
  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Additional context
Add any other context about the problem here.

Unable to run sample `Usage` code

  1. I installed the package empyrial and it is showing multiple errors
  2. I am running python 3.9.0 and I installed plotly for the visualizations
  3. I am running the sample Usage code
from empyrial import empyrial, Engine

portfolio = Engine(    
                  start_date= "2020-06-09", 
                  portfolio= ["BABA", "RELIANCE.NS", "KO", "^DJI","^IXIC"], 
                  weights = [0.2, 0.2, 0.2, 0.2, 0.2], #equal weighting by default
                  benchmark = ["SPY"] 
)

empyrial(portfolio)

Screen Shot 2021-06-08 at 8 55 13 PM

TypeError is thrown when executing the first example snippet

Describe the bug
When I execute the frist example snippet an error is thrown: TypeError: Cannot join tz-naive with tz-aware DatetimeIndex

To Reproduce
Steps to reproduce the behavior:
Execute:

from empyrial import empyrial, Engine

portfolio = Engine(
    start_date = "2022-09-01", 
    portfolio = ["BABA", "PDD", "KO", "AMD","^IXIC"], 
    weights = [0.2, 0.2, 0.2, 0.2, 0.2],  # equal weighting is set by default
    benchmark = ["SPY"]  # SPY is set by default
)

empyrial(portfolio)

Desktop (please complete the following information):

  • OS: Ubuntu 22.04
  • Browser: Chrome
  • Version: latest

Additional context

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In [2], line 10
      1 from empyrial import empyrial, Engine
      3 portfolio = Engine(
      4     start_date = "2022-09-01", 
      5     portfolio = ["BABA", "PDD", "KO", "AMD","^IXIC"], 
      6     weights = [0.2, 0.2, 0.2, 0.2, 0.2],  # equal weighting is set by default
      7     benchmark = ["SPY"]  # SPY is set by default
      8 )
---> 10 empyrial(portfolio)

File ~/.local/lib/python3.10/site-packages/empyrial.py:359, in empyrial(my_portfolio, rf, sigma_value, confidence_value)
    356 VAR = np.round(VAR, decimals=2)
    357 VAR = str(VAR * 100) + " %"
--> 359 alpha, beta = alpha_beta(returns, benchmark, risk_free=rf)
    360 AL = round(alpha, 2)
    361 BTA = round(beta, 2)

File ~/.local/lib/python3.10/site-packages/empyrical/stats.py:1046, in alpha_beta(returns, factor_returns, risk_free, period, annualization, out)
   1004 def alpha_beta(returns,
   1005                factor_returns,
   1006                risk_free=0.0,
   1007                period=DAILY,
   1008                annualization=None,
   1009                out=None):
   1010     """Calculates annualized alpha and beta.
   1011 
   1012     Parameters
   (...)
   1044     beta : float
   1045     """
-> 1046     returns, factor_returns = _aligned_series(returns, factor_returns)
   1048     return alpha_beta_aligned(
   1049         returns,
   1050         factor_returns,
   (...)
   1054         out=out,
   1055     )

File ~/.local/lib/python3.10/site-packages/empyrical/stats.py:1000, in _aligned_series(*many_series)
    995     return many_series
    997 # dataframe has no ``itervalues``
    998 return (
    999     v
-> 1000     for _, v in iteritems(pd.concat(map(_to_pandas, many_series), axis=1))
   1001 )

File ~/.local/lib/python3.10/site-packages/pandas/util/_decorators.py:317, in deprecate_nonkeyword_arguments.<locals>.decorate.<locals>.wrapper(*args, **kwargs)
    311 if len(args) > num_allow_args:
    312     warnings.warn(
    313         msg.format(arguments=arguments),
    314         FutureWarning,
    315         stacklevel=find_stack_level(inspect.currentframe()),
    316     )
--> 317 return func(*args, **kwargs)

File ~/.local/lib/python3.10/site-packages/pandas/core/reshape/concat.py:369, in concat(objs, axis, join, ignore_index, keys, levels, names, verify_integrity, sort, copy)
    147 @deprecate_nonkeyword_arguments(version=None, allowed_args=["objs"])
    148 def concat(
    149     objs: Iterable[NDFrame] | Mapping[HashableT, NDFrame],
   (...)
    158     copy: bool = True,
    159 ) -> DataFrame | Series:
    160     """
    161     Concatenate pandas objects along a particular axis.
    162 
   (...)
    367     1   3   4
    368     """
--> 369     op = _Concatenator(
    370         objs,
    371         axis=axis,
    372         ignore_index=ignore_index,
    373         join=join,
    374         keys=keys,
    375         levels=levels,
    376         names=names,
    377         verify_integrity=verify_integrity,
    378         copy=copy,
    379         sort=sort,
    380     )
    382     return op.get_result()

File ~/.local/lib/python3.10/site-packages/pandas/core/reshape/concat.py:564, in _Concatenator.__init__(self, objs, axis, join, keys, levels, names, ignore_index, verify_integrity, copy, sort)
    561 self.verify_integrity = verify_integrity
    562 self.copy = copy
--> 564 self.new_axes = self._get_new_axes()

File ~/.local/lib/python3.10/site-packages/pandas/core/reshape/concat.py:634, in _Concatenator._get_new_axes(self)
    632 def _get_new_axes(self) -> list[Index]:
    633     ndim = self._get_result_dim()
--> 634     return [
    635         self._get_concat_axis if i == self.bm_axis else self._get_comb_axis(i)
    636         for i in range(ndim)
    637     ]

File ~/.local/lib/python3.10/site-packages/pandas/core/reshape/concat.py:635, in <listcomp>(.0)
    632 def _get_new_axes(self) -> list[Index]:
    633     ndim = self._get_result_dim()
    634     return [
--> 635         self._get_concat_axis if i == self.bm_axis else self._get_comb_axis(i)
    636         for i in range(ndim)
    637     ]

File ~/.local/lib/python3.10/site-packages/pandas/core/reshape/concat.py:641, in _Concatenator._get_comb_axis(self, i)
    639 def _get_comb_axis(self, i: int) -> Index:
    640     data_axis = self.objs[0]._get_block_manager_axis(i)
--> 641     return get_objs_combined_axis(
    642         self.objs,
    643         axis=data_axis,
    644         intersect=self.intersect,
    645         sort=self.sort,
    646         copy=self.copy,
    647     )

File ~/.local/lib/python3.10/site-packages/pandas/core/indexes/api.py:105, in get_objs_combined_axis(objs, intersect, axis, sort, copy)
     81 """
     82 Extract combined index: return intersection or union (depending on the
     83 value of "intersect") of indexes on given axis, or None if all objects
   (...)
    102 Index
    103 """
    104 obs_idxes = [obj._get_axis(axis) for obj in objs]
--> 105 return _get_combined_index(obs_idxes, intersect=intersect, sort=sort, copy=copy)

File ~/.local/lib/python3.10/site-packages/pandas/core/indexes/api.py:158, in _get_combined_index(indexes, intersect, sort, copy)
    156         index = index.intersection(other)
    157 else:
--> 158     index = union_indexes(indexes, sort=False)
    159     index = ensure_index(index)
    161 if sort:

File ~/.local/lib/python3.10/site-packages/pandas/core/indexes/api.py:284, in union_indexes(indexes, sort)
    278 dti_tzs = [x for x in dtis if x.tz is not None]
    279 if len(dti_tzs) not in [0, len(dtis)]:
    280     # TODO: this behavior is not tested (so may not be desired),
    281     #  but is kept in order to keep behavior the same when
    282     #  deprecating union_many
    283     # test_frame_from_dict_with_mixed_indexes
--> 284     raise TypeError("Cannot join tz-naive with tz-aware DatetimeIndex")
    286 if len(dtis) == len(indexes):
    287     sort = True

TypeError: Cannot join tz-naive with tz-aware DatetimeIndex

Error on pip installation

Describe the bug
When installing the module using pip install empyrial I get the following error
AttributeError: module 'configparser' has no attribute 'SafeConfigParser'. Did you mean: 'RawConfigParser'?
To Reproduce
Run pip install empyrial

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: Windows
  • Visual Studio

Installation on Mac M1 Processor fails

Pip install fails on Mac M1 chip with the error
FileNotFoundError: [Errno 2] No such file or directory: 'README.md'
when it tries to install ecos==2.0.11
embotech/ecos-python#33
Workaround for this issue is to install ecos 2.0.5 version before installing empyrial
pip install ecos==2.0.5

@ssantoshp Can we add it to the installation docs for people using Mac M1 processor or adding this requirement in requirement.txt ?

Stop Auto Rebalancing

How do I stop auto rebalancing?
My problem is that I found that the total cumulative return is not the weighted average of the cumulative return of each asset.

Sharpe ratio calculation

When I read the source code of 'Sharpe ratio' function, I found that the calculation doesn't take interest rate into account. The formula for SR is E[r - r_f] / sigma, where r_f is risk free rate. I think this would affect the accuracy of results.

Issue with Pandas datareader

Describe the bug
This seems to effect all your branches

RemoteDataError Traceback (most recent call last)
in
----> 1 oracle(portfolio)

~/anaconda3/envs/empyr/lib/python3.8/site-packages/empyrial.py in oracle(my_portfolio, prediction_days, based_on)
334
335
--> 336 df = web.DataReader(asset, data_source='yahoo', start = my_portfolio.start_date, end= my_portfolio.end_date)
337 df = pd.DataFrame(df)
338 df.reset_index(level=0, inplace=True)

~/anaconda3/envs/empyr/lib/python3.8/site-packages/pandas/util/_decorators.py in wrapper(*args, **kwargs)
197 else:
198 kwargs[new_arg_name] = new_arg_value
--> 199 return func(*args, **kwargs)
200
201 return cast(F, wrapper)

~/anaconda3/envs/empyr/lib/python3.8/site-packages/pandas_datareader/data.py in DataReader(name, data_source, start, end, retry_count, pause, session, api_key)
374
375 if data_source == "yahoo":
--> 376 return YahooDailyReader(
377 symbols=name,
378 start=start,

~/anaconda3/envs/empyr/lib/python3.8/site-packages/pandas_datareader/base.py in read(self)
251 # If a single symbol, (e.g., 'GOOG')
252 if isinstance(self.symbols, (string_types, int)):
--> 253 df = self._read_one_data(self.url, params=self._get_params(self.symbols))
254 # Or multiple symbols, (e.g., ['GOOG', 'AAPL', 'MSFT'])
255 elif isinstance(self.symbols, DataFrame):

~/anaconda3/envs/empyr/lib/python3.8/site-packages/pandas_datareader/yahoo/daily.py in _read_one_data(self, url, params)
151 url = url.format(symbol)
152
--> 153 resp = self._get_response(url, params=params)
154 ptrn = r"root.App.main = (.*?);\n}(this));"
155 try:

~/anaconda3/envs/empyr/lib/python3.8/site-packages/pandas_datareader/base.py in _get_response(self, url, params, headers)
179 msg += "\nResponse Text:\n{0}".format(last_response_text)
180
--> 181 raise RemoteDataError(msg)
182
183 def _get_crumb(self, *args):

RemoteDataError: Unable to read URL: https://finance.yahoo.com/quote/BABA/history?period1=1591671600&period2=1625972399&interval=1d&frequency=1d&filter=history
Response Text:
b'\n \n \n \n <title>Yahoo</title>\n \n \n <style>\n html {\n height: 100%;\n }\n body {\n background: #fafafc url(https://s.yimg.com/nn/img/sad-panda-201402200631.png) 50% 50%;\n background-size: cover;\n height: 100%;\n text-align: center;\n font: 300 18px "helvetica neue", helvetica, verdana, tahoma, arial, sans-serif;\n }\n table {\n height: 100%;\n width: 100%;\n table-layout: fixed;\n border-collapse: collapse;\n border-spacing: 0;\n border: none;\n }\n h1 {\n font-size: 42px;\n font-weight: 400;\n color: #400090;\n }\n p {\n color: #1A1A1A;\n }\n #message-1 {\n font-weight: bold;\n margin: 0;\n }\n #message-2 {\n display: inline-block;\n *display: inline;\n zoom: 1;\n max-width: 17em;\n _width: 17em;\n }\n </style>\n <script>\n document.write('');var beacon = new Image();beacon.src="//bcn.fp.yahoo.com/p?s=1197757129&t="+new Date().getTime()+"&src=aws&err_url="+encodeURIComponent(document.URL)+"&err=%&test="+encodeURIComponent('%<{Bucket}cqh[:200]>');\n </script>\n \n \n \n \n

\n \n \n \n
\n Yahoo Logo\n

Will be right back...

\n

Thank you for your patience.

\n

Our engineers are working quickly to resolve the issue.

\n
\n '

1

Error when running fundlens

All functions ran perfectly except for fundlens. I got the following error:


KeyError Traceback (most recent call last)
in
8 )
9
---> 10 fundlens(portfolio)

~/anaconda3/lib/python3.8/site-packages/empyrial.py in fundlens(my_portfolio, period)
484
485 #book value
--> 486 book_value = yahoo_financials.get_book_value()
487
488 #operating income

~/anaconda3/lib/python3.8/site-packages/yahoofinancials/init.py in get_book_value(self)
794 # Financial Statement Data Methods
795 def get_book_value(self):
--> 796 return self._financial_statement_data('balance', 'balanceSheetHistoryQuarterly',
797 'totalStockholderEquity', 'quarterly')
798

~/anaconda3/lib/python3.8/site-packages/yahoofinancials/init.py in _financial_statement_data(self, stmt_type, stmt_code, field_name, freq)
685 except (IndexError, AttributeError, TypeError):
686 date_key = list(re_data[self.ticker][0])[0]
--> 687 data = re_data[self.ticker][0][date_key][field_name]
688 else:
689 data = {}

KeyError: 'totalStockholderEquity'

Unlisted Stock Symbol Counted in Pie Chart

Hi ,
awesome tool santosh bhai.
If a ticker symbol data is not listed at the time of the start date , it stills counts the ticker in the pie chart portfolio. Ideally it should not .. or am i getting this wrong.
very new guy.
regards ,

Custom weighting not working

Describe the bug
Asset allocation is equally weighted when no optimisation method is provided.

To Reproduce
Use the documented example for custom asset allocation.

portfolio = Engine(
      start_date = "2018-01-01",
      portfolio= ["BABA", "PDD", "KO", "AMD","^IXIC"], 
      weights = [0.1, 0.3, 0.15, 0.25, 0.2], #custom weights
)

Expected behavior
portfolio.weights equals to [0.1, 0.3, 0.15, 0.25, 0.2]

Actual behavior
portfolio.weights equals to [0.2, 0.2, 0.2, 0.2, 0.2]

add daily and weekly rebalance period

I committed a change before for adding daily and weekly rebalancing. The commit got approved and implemented but I still cannot use that feature. I guess we would need further modifications in the empyrial.py file.

Graph styling

Is there a way to override the default styling parameters used in your tearsheet? I understand that most of the styling is inherited from quantstats. Anyway you can suggest how to change things like facecolor, linewidth, etc?

Versioneer broken when installing on python 3.12.0

When running pip install empyrial, below error message is given:

Collecting empyrial
  Using cached empyrial-2.1.3-py3-none-any.whl.metadata (614 bytes)
Requirement already satisfied: numpy in ./miniconda3/envs/revesture1/lib/python3.12/site-packages (from empyrial) (1.26.2)
Requirement already satisfied: matplotlib in ./miniconda3/envs/revesture1/lib/python3.12/site-packages (from empyrial) (3.8.2)
Collecting datetime (from empyrial)
  Using cached DateTime-5.4-py3-none-any.whl.metadata (33 kB)
Collecting empyrical (from empyrial)
  Using cached empyrical-0.5.5.tar.gz (52 kB)
  Preparing metadata (setup.py) ... error
  error: subprocess-exited-with-error

  × python setup.py egg_info did not run successfully.
  │ exit code: 1
  ╰─> [18 lines of output]
      /private/var/folders/sy/49bw_1y55j7fyzdy080n79p80000gn/T/pip-install-sblzswrz/empyrical_d194ecdd5052487aa8e80c4ca0cdb39f/versioneer.py:485: SyntaxWarning: invalid escape sequence '\s'
        LONG_VERSION_PY['git'] = '''
      Traceback (most recent call last):
        File "<string>", line 2, in <module>
        File "<pip-setuptools-caller>", line 34, in <module>
        File "/private/var/folders/sy/49bw_1y55j7fyzdy080n79p80000gn/T/pip-install-sblzswrz/empyrical_d194ecdd5052487aa8e80c4ca0cdb39f/setup.py", line 79, in <module>
          version=versioneer.get_version(),
                  ^^^^^^^^^^^^^^^^^^^^^^^^
        File "/private/var/folders/sy/49bw_1y55j7fyzdy080n79p80000gn/T/pip-install-sblzswrz/empyrical_d194ecdd5052487aa8e80c4ca0cdb39f/versioneer.py", line 1473, in get_version
          return get_versions()["version"]
                 ^^^^^^^^^^^^^^
        File "/private/var/folders/sy/49bw_1y55j7fyzdy080n79p80000gn/T/pip-install-sblzswrz/empyrical_d194ecdd5052487aa8e80c4ca0cdb39f/versioneer.py", line 1406, in get_versions
          cfg = get_config_from_root(root)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/private/var/folders/sy/49bw_1y55j7fyzdy080n79p80000gn/T/pip-install-sblzswrz/empyrical_d194ecdd5052487aa8e80c4ca0cdb39f/versioneer.py", line 412, in get_config_from_root
          parser = configparser.SafeConfigParser()
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      AttributeError: module 'configparser' has no attribute 'SafeConfigParser'. Did you mean: 'RawConfigParser'?
      [end of output]

  note: This error originates from a subprocess, and is likely not a problem with pip.
error: metadata-generation-failed

× Encountered error while generating package metadata.
╰─> See above for output.

note: This is an issue with the package mentioned above, not pip.
hint: See above for details.

I think versioneer needs to be removed and replaced with setuptools_scm as mentioned in below post with similar issue: pydata/pandas-datareader#969 (comment)

Rebalancing error.

Describe the bug
`from empyrial import empyrial, Engine

portfolio = Engine(
start_date = "2020-01-01",
end_date = "2023-02-25",
portfolio = ["CNQ.TO","MFC.TO","SHOP.TO","CNR.TO","CVE.TO","BCE.TO","ABX.TO","TOU.TO","ENB.TO","RY.TO","ATVI","TRP.TO","SU.TO"],
benchmark = ["^GSPTSE"], #SPY is set by default
rebalance = "1y" #rebalance every month
)

empyrial(portfolio)`

`AttributeError Traceback (most recent call last)
in
1 from empyrial import empyrial, Engine
2
----> 3 portfolio = Engine(
4 start_date = "2020-01-01",
5 end_date = "2023-02-25",

2 frames
/usr/local/lib/python3.8/dist-packages/empyrial.py in valid_range(start_date, end_date, rebalance)
753
754 # have to make end date a datetime because strptime is not supported for date
--> 755 end_date = dt.datetime(end_date.year, end_date.month, end_date.day)
756
757 # gets the number of days

AttributeError: 'str' object has no attribute 'year'`

Using the Google Colab link on your homepage.

get_report error

Describe the bug
the sample code (as per --> https://empyrial.gitbook.io/empyrial/save-the-tearsheet/get-a-report) is throwing an error

To Reproduce
Steps to reproduce the behavior:

  1. Go to 'https://empyrial.gitbook.io/empyrial/save-the-tearsheet/get-a-report...'
  2. Run the sample code
  3. Scroll down to '....'
  4. See error

NameError Traceback (most recent call last)
/var/folders/41/q1hx0rjd5xzck1vl121t6b2m0000gn/T/ipykernel_11664/2518190224.py in
10 empyrial(portfolio)
11
---> 12 get_report(portfolio)

NameError: name 'get_report' is not defined

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: [e.g. iOS]
  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Additional context
using jypiterlab notebook

Failed to build scs ERROR: Could not build wheels for scs which use PEP 517 and cannot be installed directly

I am using Python 3.8.10.
I had a separate environment and I got the following error when pip installing empyrial
Failed to build scs
ERROR: Could not build wheels for scs which use PEP 517 and cannot be installed directly

From this link pydata/bottleneck#281
I tried pip install --upgrade pip setuptools wheel but I am still getting the same bug when installing empyrial.

  • OS: Ubuntu 20.04
  • mini conda version and a separate environment for trading
  • Python 3.8.10
  • Let me know if there is any way around this bug.
    Thanks

Error with stocks data

First and foremost, I wanted to let you know how much I adore your work and how big of a fan I am of your Empyrial library on Python! I use it quite often for my analyses.

I'm reaching out today because I've been facing an issue when running the code, regardless of the date I choose: it indicates there's a data problem. The specific issue is as follows:

ValueError Traceback (most recent call last)
in <cell line: 27>()
25 )
26 benchmark=["SPY"]
---> 27 empyrial(portfolio)
28
29

1 frames
/usr/local/lib/python3.10/dist-packages/empyrial/main.py in get_returns(stocks, wts, start_date, end_date)
148 initial_alloc = wts/assets.iloc[0]
149 if initial_alloc.isna().any():
--> 150 raise ValueError("Some stock is not available at initial state!")
151 portfolio_value = (assets * initial_alloc).sum(axis=1)
152 returns = portfolio_value.pct_change()[1:]

ValueError: Some stock is not available at initial state!

I've tried to resolve it by using more recent dates to download titles, but it still doesn't work.

Buy and Hold Comparison

A buy and hold comparison of returns may be useful to compared with the various optimisation strategies

Can't use empyrial function when specifying rebalance

Describe the bug
When I specify the rebalance parameter when creating a portfolio object, the empyrial function will return
"ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all()."

To Reproduce

  1. I define portfolio as such
    from empyrial import empyrial, Engine
    import pandas as pd
    portfolio = Engine(
    start_date = "2018-08-01",
    portfolio = ["BABA", "PDD", "KO", "AMD","^IXIC"],
    optimizer = "EF", # rebalance every year
    rebalance = '1y'
    )
  2. I run the empyrial function and it returns an error.
    empyrial(portfolio)

ValueError Traceback (most recent call last)
Cell In[39], line 1
----> 1 empyrial(portfolio)

File ~\anaconda3\envs\myenv\lib\site-packages\empyrial\main.py:192, in empyrial(my_portfolio, rf, sigma_value, confidence_value, report, filename)
191 def empyrial(my_portfolio, rf=0.0, sigma_value=1, confidence_value=0.95, report=False, filename="empyrial_report.pdf"):
--> 192 if my_portfolio.rebalance != None:
193 # we want to get the dataframe with the dates and weights
194 rebalance_schedule = my_portfolio.rebalance
196 columns = []

File ~\anaconda3\envs\myenv\lib\site-packages\pandas\core\generic.py:1527, in NDFrame.nonzero(self)
1525 @Final
1526 def nonzero(self) -> NoReturn:
-> 1527 raise ValueError(
1528 f"The truth value of a {type(self).name} is ambiguous. "
1529 "Use a.empty, a.bool(), a.item(), a.any() or a.all()."
1530 )

ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

Expected behavior
However, when I remove the rebalance parameter, I get the expected output: metrics, graphs, everything, ex:
from empyrial import empyrial, Engine
import pandas as pd
portfolio = Engine(
start_date = "2018-08-01",
portfolio = ["BABA", "PDD", "KO", "AMD","^IXIC"],
optimizer = "EF"
)
empyrial(portfolio)

Other details
I found that if I modify this part:
if my_portfolio.rebalance != None: [...]
to
if isinstance(my_portfolio.rebalance, pd.DataFrame): [...]
It seems to work properly?

I'm running this via dataspell in an anaconda environment (2.5.0) on a windows PC.
I'm an economics student and this is my first github post so let me know if you need any additional information

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.